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 continue;
206 }
207 }
208 } else if let Some(ip4) = temp_pot_conf.ip4 {
209 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 continue;
223 }
224 }
225 } else {
226 continue;
228 }
229 v.push(pot_conf);
230 }
231 v
232}