Skip to main content

toe_beans/v4/server/
config.rs

1use crate::v4::{LeaseConfig, MessageOptions, SocketConfig};
2use log::{info, warn};
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::fs::{OpenOptions, read_to_string};
6use std::io::Write;
7use std::net::Ipv4Addr;
8use std::os::unix::fs::OpenOptionsExt;
9use std::path::PathBuf;
10use toml;
11
12/// Configuration used specifically within a `Server`.
13/// This differs from `ServerConfig` which bundles many configs, including:
14/// 'InnerServerConfig`, `SocketConfig`, and `LeasesConfig`.
15#[derive(Serialize, Deserialize, Debug)]
16pub struct InnerServerConfig {
17    /// Controls whether the server agrees to use [Rapid Commit](crate::v4::MessageOptions::RapidCommit).
18    pub rapid_commit: bool,
19    /// May or may not match the IP address in `SocketConfig.listen_address`.
20    /// For example, you might be listening to `0.0.0.0`, but your server has another address it can be reached from.
21    /// This is used in both the `siaddr` field and `ServerIdentifier` option.
22    ///
23    /// If `None`, then `listen_address` is used.
24    pub server_address: Option<Ipv4Addr>,
25    /// DHCP option numbers (as a String) mapped to values that are used to respond to parameter request list options
26    pub parameters: HashMap<u8, MessageOptions>,
27}
28
29impl Default for InnerServerConfig {
30    fn default() -> Self {
31        Self {
32            rapid_commit: true,
33            server_address: None,
34            parameters: HashMap::with_capacity(u8::MAX as usize),
35        }
36    }
37}
38
39/// Representation of a `toe-beans.toml` file. Used by the [Server](crate::v4::Server).
40///
41/// This is exported for you to use in your application to generate a `toe-beans.toml`.
42#[derive(Serialize, Deserialize, Debug)]
43pub struct ServerConfig {
44    /// The path to a directory where this config will exist on disk.
45    #[serde(skip)]
46    pub path: PathBuf,
47    /// A subtable of configuration that is specific to, and used by, the `Server`.
48    pub server: InnerServerConfig,
49    /// A subtable of configuration that is specific to, and passed to, a `Socket` by both a `Server` and a `Client`.
50    pub socket_config: SocketConfig,
51    /// A subtable of configuration that is specific to, and passed to, a `Leases` by the `Server`.
52    pub lease_config: LeaseConfig,
53}
54
55impl Default for ServerConfig {
56    fn default() -> Self {
57        Self {
58            path: PathBuf::new(), // empty, the current directory
59            server: InnerServerConfig::default(),
60            socket_config: SocketConfig::default_server(),
61            lease_config: LeaseConfig::default(),
62        }
63    }
64}
65
66impl ServerConfig {
67    /// The file name that the Config is read from and written to.
68    pub const FILE_NAME: &'static str = "toe-beans.toml";
69
70    /// Reads the `toe-beans.toml` file and deserializes it into a Config.
71    /// Returns default options if there are any issues.
72    pub fn read(from_where: PathBuf) -> Self {
73        let read_result = read_to_string(from_where.join(Self::FILE_NAME));
74        if let Ok(config_string) = read_result {
75            let toml_result = toml::from_str(&config_string);
76            match toml_result {
77                Ok(config) => {
78                    return Self {
79                        path: from_where, // from_where might be different if passed through cli args
80                        ..config
81                    };
82                }
83                Err(error) => {
84                    warn!("{error}");
85                    warn!(
86                        "Warning: invalid {}. Using default values.",
87                        Self::FILE_NAME
88                    );
89                }
90            }
91        } else {
92            warn!(
93                "Warning: can't read {}. Using default values.",
94                Self::FILE_NAME
95            );
96        }
97
98        Self {
99            path: from_where,
100            ..ServerConfig::default()
101        }
102    }
103
104    /// Serializes a Config and writes it to a `toe-beans.toml` file.
105    pub fn write(&self) {
106        info!("Writing {}", Self::FILE_NAME);
107
108        let config_content = toml::to_string_pretty(self).expect("Failed to generate toml data");
109        let mode = 0o600;
110
111        let mut file = OpenOptions::new()
112            .read(false)
113            .write(true)
114            .create(true)
115            .truncate(true)
116            .mode(mode) // ensure that file permissions are set before creating file
117            .open(self.path.join(Self::FILE_NAME))
118            .unwrap_or_else(|_| panic!("Failed to open config {} for writing", Self::FILE_NAME));
119
120        file.write_all(config_content.as_bytes())
121            .unwrap_or_else(|_| panic!("Failed to write config to {}", Self::FILE_NAME));
122    }
123}