pot_rs/
lib.rs

1pub mod bridge;
2pub mod error;
3mod system;
4pub(crate) mod util;
5
6use ipnet::IpNet;
7use std::convert::TryFrom;
8use std::default::Default;
9use std::fs::File;
10use std::io::prelude::*;
11use std::net::IpAddr;
12use std::path::PathBuf;
13use std::process::{Command, Stdio};
14use std::str::FromStr;
15use walkdir::WalkDir;
16
17pub type Result<T> = ::std::result::Result<T, error::PotError>;
18
19#[derive(Debug, Clone)]
20pub struct PotSystemConfig {
21    pub zfs_root: String,
22    pub fs_root: String,
23    pub network: IpNet,
24    pub netmask: IpAddr,
25    pub gateway: IpAddr,
26    pub ext_if: String,
27    pub dns_name: String,
28    pub dns_ip: IpAddr,
29}
30
31impl PotSystemConfig {
32    pub fn from_system() -> Result<Self> {
33        let psc = system::PartialSystemConf::new();
34        PotSystemConfig::try_from(psc)
35    }
36}
37
38impl TryFrom<system::PartialSystemConf> for PotSystemConfig {
39    type Error = error::PotError;
40
41    fn try_from(psc: system::PartialSystemConf) -> std::result::Result<Self, Self::Error> {
42        if psc.is_valid() {
43            Ok(PotSystemConfig {
44                zfs_root: psc.zfs_root.unwrap(),
45                fs_root: psc.fs_root.unwrap(),
46                network: psc.network.unwrap(),
47                netmask: psc.netmask.unwrap(),
48                gateway: psc.gateway.unwrap(),
49                ext_if: psc.ext_if.unwrap(),
50                dns_name: psc.dns_name.unwrap(),
51                dns_ip: psc.dns_ip.unwrap(),
52            })
53        } else {
54            Err(error::PotError::IncompleteSystemConf)
55        }
56    }
57}
58
59#[derive(Debug, PartialEq, Eq)]
60pub enum NetType {
61    Inherit,
62    Alias,
63    PublicBridge,
64    PrivateBridge,
65}
66
67#[derive(Debug)]
68pub struct PotConf {
69    pub name: String,
70    pub ip_addr: Option<IpAddr>,
71    pub network_type: NetType,
72}
73
74#[derive(Debug, Default)]
75pub struct PotConfVerbatim {
76    pub vnet: Option<String>,
77    pub ip4: Option<String>,
78    pub ip: Option<String>,
79    pub network_type: Option<String>,
80}
81
82impl Default for PotConf {
83    fn default() -> PotConf {
84        PotConf {
85            name: String::default(),
86            ip_addr: None,
87            network_type: NetType::Inherit,
88        }
89    }
90}
91
92fn get_pot_path_list(conf: &PotSystemConfig) -> Vec<PathBuf> {
93    let mut result = Vec::new();
94    let fsroot = conf.fs_root.clone();
95    WalkDir::new(fsroot + "/jails")
96        .max_depth(1)
97        .min_depth(1)
98        .into_iter()
99        .filter_map(std::result::Result::ok)
100        .filter(|x| x.file_type().is_dir())
101        .for_each(|x| result.push(x.into_path()));
102    result
103}
104
105pub fn get_pot_list(conf: &PotSystemConfig) -> Vec<String> {
106    let mut result = Vec::new();
107    for pot_dir in get_pot_path_list(conf) {
108        if let Some(pot_name) = pot_dir.file_name() {
109            if let Some(pot_name_str) = pot_name.to_str() {
110                result.push(pot_name_str.to_string());
111            }
112        }
113    }
114    result
115}
116
117fn is_pot_running(pot_name: &str) -> Result<bool> {
118    let status = Command::new("/usr/sbin/jls")
119        .arg("-j")
120        .arg(pot_name)
121        .stdin(Stdio::null())
122        .stdout(Stdio::piped())
123        .stderr(Stdio::null())
124        .status();
125    if let Ok(status) = status {
126        Ok(status.success())
127    } else {
128        Err(error::PotError::JlsError)
129    }
130}
131
132pub fn get_running_pot_list(conf: &PotSystemConfig) -> Vec<String> {
133    let mut result = Vec::new();
134    for pot in get_pot_list(conf) {
135        if let Ok(status) = is_pot_running(&pot) {
136            if status {
137                result.push(pot);
138            }
139        }
140    }
141    result
142}
143
144pub fn get_pot_conf_list(conf: PotSystemConfig) -> Vec<PotConf> {
145    let mut v: Vec<PotConf> = Vec::new();
146
147    let fsroot = conf.fs_root.clone();
148    let pdir = fsroot + "/jails/";
149    for mut dir_path in get_pot_path_list(&conf) {
150        let mut pot_conf = PotConf {
151            name: dir_path
152                .clone()
153                .strip_prefix(&pdir)
154                .ok()
155                .unwrap()
156                .to_str()
157                .unwrap()
158                .to_string(),
159            ..Default::default()
160        };
161        dir_path.push("conf");
162        dir_path.push("pot.conf");
163        let mut conf_file = match File::open(dir_path.as_path()) {
164            Ok(x) => x,
165            Err(_) => continue,
166        };
167        let mut conf_str = String::new();
168        match conf_file.read_to_string(&mut conf_str) {
169            Ok(_) => (),
170            Err(_) => continue,
171        }
172        let mut temp_pot_conf = PotConfVerbatim::default();
173        for s in conf_str.lines() {
174            if s.starts_with("ip4=") {
175                temp_pot_conf.ip4 = Some(s.split('=').nth(1).unwrap().to_string());
176            }
177            if s.starts_with("ip=") {
178                temp_pot_conf.ip = Some(s.split('=').nth(1).unwrap().to_string());
179            }
180            if s.starts_with("vnet=") {
181                temp_pot_conf.vnet = Some(s.split('=').nth(1).unwrap().to_string());
182            }
183            if s.starts_with("network_type=") {
184                temp_pot_conf.network_type = Some(s.split('=').nth(1).unwrap().to_string());
185            }
186        }
187        if let Some(network_type) = temp_pot_conf.network_type {
188            pot_conf.network_type = match network_type.as_str() {
189                "inherit" => NetType::Inherit,
190                "alias" => NetType::Alias,
191                "public-bridge" => NetType::PublicBridge,
192                "private-bridge" => NetType::PrivateBridge,
193                _ => continue,
194            };
195            if pot_conf.network_type == NetType::Alias {
196                continue;
197            }
198            if pot_conf.network_type == NetType::PublicBridge
199                || pot_conf.network_type == NetType::PrivateBridge
200            {
201                if let Some(ip_addr) = temp_pot_conf.ip {
202                    pot_conf.ip_addr = Some(IpAddr::from_str(&ip_addr).ok().unwrap())
203                } else {
204                    // Error !
205                    continue;
206                }
207            }
208        } else if let Some(ip4) = temp_pot_conf.ip4 {
209            // Old pot version - compatibility mode
210            if &ip4 == "inherit" {
211                pot_conf.network_type = NetType::Inherit;
212            } else {
213                pot_conf.ip_addr = Some(IpAddr::from_str(&ip4).ok().unwrap());
214                if let Some(vnet) = temp_pot_conf.vnet {
215                    if &vnet == "true" {
216                        pot_conf.network_type = NetType::PublicBridge;
217                    } else {
218                        pot_conf.network_type = NetType::Alias;
219                    }
220                } else {
221                    // Error
222                    continue;
223                }
224            }
225        } else {
226            // Error !
227            continue;
228        }
229        v.push(pot_conf);
230    }
231    v
232}