From ddd2f09972f1c67ee562a3ccf5c22c92890b4eaa Mon Sep 17 00:00:00 2001 From: PradeepKiruvale Date: Wed, 29 Jun 2022 16:46:40 +0530 Subject: c8y_configuration_plugin --init and c8y_log_plugin --init are overwriting exiting files (#1217) --init should not overwrite the existing files Signed-off-by: Pradeep Kumar K J --- crates/common/tedge_utils/src/file.rs | 83 ++++++++++++++++++++-------- crates/core/tedge_mapper/src/c8y/mapper.rs | 2 + plugins/c8y_configuration_plugin/src/main.rs | 27 +++++---- plugins/c8y_log_plugin/src/main.rs | 55 ++++++------------ 4 files changed, 92 insertions(+), 75 deletions(-) diff --git a/crates/common/tedge_utils/src/file.rs b/crates/common/tedge_utils/src/file.rs index 6b714750..902d10c0 100644 --- a/crates/common/tedge_utils/src/file.rs +++ b/crates/common/tedge_utils/src/file.rs @@ -1,5 +1,5 @@ use nix::unistd::*; -use std::fs::File; +use std::io::Write; use std::os::linux::fs::MetadataExt; use std::os::unix::fs::PermissionsExt; use std::path::{Path, PathBuf}; @@ -28,6 +28,9 @@ pub enum FileError { #[error("The path is not accessible. {path:?}")] PathNotAccessible { path: PathBuf }, + + #[error("Writing the content to the file failed: {file:?}.")] + WriteContentFailed { file: String, from: std::io::Error }, } pub fn create_directory_with_user_group( @@ -37,14 +40,12 @@ pub fn create_directory_with_user_group( mode: u32, ) -> Result<(), FileError> { let perm_entry = PermissionEntry::new(Some(user.into()), Some(group.into()), Some(mode)); - let () = perm_entry.create_directory(Path::new(dir))?; - Ok(()) + perm_entry.create_directory(Path::new(dir)) } pub fn create_directory_with_mode(dir: &str, mode: u32) -> Result<(), FileError> { let perm_entry = PermissionEntry::new(None, None, Some(mode)); - let () = perm_entry.create_directory(Path::new(dir))?; - Ok(()) + perm_entry.create_directory(Path::new(dir)) } pub fn create_file_with_user_group( @@ -52,16 +53,10 @@ pub fn create_file_with_user_group( user: &str, group: &str, mode: u32, + default_content: Option<&str>, ) -> Result<(), FileError> { let perm_entry = PermissionEntry::new(Some(user.into()), Some(group.into()), Some(mode)); - let () = perm_entry.create_file(Path::new(file))?; - Ok(()) -} - -pub fn create_file_with_mode(file: &str, mode: u32) -> Result<(), FileError> { - let perm_entry = PermissionEntry::new(None, None, Some(mode)); - let () = perm_entry.create_file(Path::new(file))?; - Ok(()) + perm_entry.create_file(Path::new(file), default_content) } #[derive(Debug, PartialEq, Eq, Default, Clone)] @@ -111,12 +106,31 @@ impl PermissionEntry { } } - fn create_file(&self, file: &Path) -> Result<(), FileError> { - match File::create(file) { - Ok(_) => { + /// This function creates a file with a given path, specific access privileges and with the given content. + /// If the file already exists, then it will not be re-created and it will not overwrite/append the contents of the file. + /// This method returns + /// Ok() when file is created and the content is written successfully into the file. + /// Ok() when the file aleady exists + /// Err(_) When it can not create the file with the appropriate owner and access permissions. + fn create_file(&self, file: &Path, default_content: Option<&str>) -> Result<(), FileError> { + match fs::OpenOptions::new() + .create_new(true) + .write(true) + .open(file) + { + Ok(mut f) => { let () = self.apply(file)?; + if let Some(default_content) = default_content { + f.write(default_content.as_bytes()).map_err(|e| { + FileError::WriteContentFailed { + file: file.display().to_string(), + from: e, + } + })?; + } Ok(()) } + Err(e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()), Err(e) => Err(FileError::FileCreateFailed { file: file.display().to_string(), @@ -229,7 +243,7 @@ mod tests { let file_path = temp_dir.path().join("file").display().to_string(); let user = whoami::username(); - let _ = create_file_with_user_group(file_path.as_str(), &user, &user, 0o644).unwrap(); + let _ = create_file_with_user_group(file_path.as_str(), &user, &user, 0o644, None).unwrap(); assert!(Path::new(file_path.as_str()).exists()); let meta = std::fs::metadata(file_path.as_str()).unwrap(); let perm = meta.permissions(); @@ -237,14 +251,39 @@ mod tests { assert!(format!("{:o}", perm.mode()).contains("644")); } + #[test] + fn create_file_with_default_content() { + let temp_dir = TempDir::new().unwrap(); + let file_path = temp_dir.path().join("file").display().to_string(); + let user = whoami::username(); + + let example_config = r#"# Add the configurations to be managed by c8y-configuration-plugin + files = [ + # { path = '/etc/tedge/tedge.toml' }, + ]"#; + + // Create a new file with default content + create_file_with_user_group( + file_path.as_str(), + &user, + &user, + 0o775, + Some(&example_config.to_string()), + ) + .unwrap(); + + let content = fs::read(file_path).unwrap(); + assert_eq!(example_config.as_bytes(), content); + } + #[test] fn create_file_wrong_user() { let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join("file").display().to_string(); let user = whoami::username(); - let err = - create_file_with_user_group(file_path.as_str(), "test", &user, 0o775).unwrap_err(); + let err = create_file_with_user_group(file_path.as_str(), "test", &user, 0o775, None) + .unwrap_err(); assert!(err.to_string().contains("User not found")); } @@ -255,8 +294,8 @@ mod tests { let file_path = temp_dir.path().join("file").display().to_string(); let user = whoami::username(); - let err = - create_file_with_user_group(file_path.as_str(), &user, "test", 0o775).unwrap_err(); + let err = create_file_with_user_group(file_path.as_str(), &user, "test", 0o775, None) + .unwrap_err(); assert!(err.to_string().contains("Group not found")); fs::remove_file(file_path.as_str()).unwrap(); @@ -309,7 +348,7 @@ mod tests { let file_path = temp_dir.path().join("file").display().to_string(); let user = whoami::username(); - let _ = create_file_with_user_group(file_path.as_str(), &user, &user, 0o644).unwrap(); + let _ = create_file_with_user_group(file_path.as_str(), &user, &user, 0o644, None).unwrap(); assert!(Path::new(file_path.as_str()).exists()); let meta = fs::metadata(file_path.as_str()).unwrap(); diff --git a/crates/core/tedge_mapper/src/c8y/mapper.rs b/crates/core/tedge_mapper/src/c8y/mapper.rs index 8ea3b4fc..81068adb 100644 --- a/crates/core/tedge_mapper/src/c8y/mapper.rs +++ b/crates/core/tedge_mapper/src/c8y/mapper.rs @@ -110,12 +110,14 @@ fn create_directories(config_dir: &str) -> Result<(), anyhow::Error> { "tedge", "tedge", 0o644, + None, )?; create_file_with_user_group( &format!("{config_dir}/operations/c8y/c8y_Restart"), "tedge", "tedge", 0o644, + None, )?; Ok(()) } diff --git a/plugins/c8y_configuration_plugin/src/main.rs b/plugins/c8y_configuration_plugin/src/main.rs index 96d2b020..45128f85 100644 --- a/plugins/c8y_configuration_plugin/src/main.rs +++ b/plugins/c8y_configuration_plugin/src/main.rs @@ -14,7 +14,6 @@ use c8y_smartrest::smartrest_deserializer::{ use c8y_smartrest::topic::C8yTopic; use clap::Parser; use mqtt_channel::{Message, SinkExt, StreamExt, Topic}; -use std::fs; use std::path::{Path, PathBuf}; use tedge_config::{ ConfigRepository, ConfigSettingAccessor, MqttPortSetting, TEdgeConfig, TmpPathSetting, @@ -212,26 +211,24 @@ fn init(cfg_dir: PathBuf) -> Result<(), anyhow::Error> { } fn create_operation_files(config_dir: &str) -> Result<(), anyhow::Error> { - create_directory_with_user_group(&format!("{config_dir}/c8y"), "root", "root", 0o775)?; + create_directory_with_user_group(&format!("{config_dir}/c8y"), "root", "root", 0o1777)?; + let example_config = r#"# Add the configurations to be managed by c8y-configuration-plugin + files = [ + # { path = '/etc/tedge/tedge.toml' }, + # { path = '/etc/tedge/mosquitto-conf/c8y-bridge.conf', type = 'c8y-bridge.conf' }, + # { path = '/etc/tedge/mosquitto-conf/tedge-mosquitto.conf', type = 'tedge-mosquitto.conf' }, + # { path = '/etc/mosquitto/mosquitto.conf', type = 'mosquitto.conf' }, + # { path = '/etc/tedge/c8y/example.txt', type = 'example', user = 'tedge', group = 'tedge', mode = 0o444 } + ]"#; + create_file_with_user_group( &format!("{config_dir}/c8y/c8y-configuration-plugin.toml"), "root", "root", 0o644, + Some(example_config), )?; - let example_config = r#"# Add the configurations to be managed by c8y-configuration-plugin -files = [ -# { path = '/etc/tedge/tedge.toml' }, -# { path = '/etc/tedge/mosquitto-conf/c8y-bridge.conf', type = 'c8y-bridge.conf' }, -# { path = '/etc/tedge/mosquitto-conf/tedge-mosquitto.conf', type = 'tedge-mosquitto.conf' }, -# { path = '/etc/mosquitto/mosquitto.conf', type = 'mosquitto.conf' }, -# { path = '/etc/tedge/c8y/example.txt', type = 'example', user = 'tedge', group = 'tedge', mode = 0o444 } -]"#; - fs::write( - &format!("{config_dir}/c8y/c8y-configuration-plugin.toml"), - example_config, - )?; create_directory_with_user_group( &format!("{config_dir}/operations/c8y"), "tedge", @@ -243,12 +240,14 @@ files = [ "tedge", "tedge", 0o644, + None, )?; create_file_with_user_group( &format!("{config_dir}/operations/c8y/c8y_DownloadConfigFile"), "tedge", "tedge", 0o644, + None, )?; Ok(()) } diff --git a/plugins/c8y_log_plugin/src/main.rs b/plugins/c8y_log_plugin/src/main.rs index 42e28053..acee7dbf 100644 --- a/plugins/c8y_log_plugin/src/main.rs +++ b/plugins/c8y_log_plugin/src/main.rs @@ -11,11 +11,7 @@ use clap::Parser; use inotify::{EventMask, EventStream}; use inotify::{Inotify, WatchMask}; use mqtt_channel::{Connection, StreamExt}; -use std::{ - fs::OpenOptions, - io::Write, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use tedge_config::{ ConfigRepository, ConfigSettingAccessor, LogPathSetting, MqttPortSetting, TEdgeConfig, DEFAULT_TEDGE_CONFIG_PATH, @@ -191,27 +187,6 @@ fn init(config_dir: &Path, logs_dir: &Path) -> Result<(), anyhow::Error> { Ok(()) } -/// append the log plugin file with software-management logs -/// assumes file is already created. -fn create_default_log_plugin_file(path_to_toml: &str, logs_dir: &str) -> Result<(), anyhow::Error> { - let logs_path = format!("{logs_dir}/tedge/agent/software-*"); - let data = toml::toml! { - files = [ - { type = "software-management", path = logs_path } - ] - }; - - let mut toml_file = OpenOptions::new() - .append(true) - .create(false) - .open(path_to_toml) - .map_err(|error| { - anyhow::anyhow!("Unable to open file: {}. Error: {}", path_to_toml, error) - })?; - toml_file.write_all(data.to_string().as_bytes())?; - Ok(()) -} - /// for the log plugin to work the following directories and files are needed: /// /// Directories: @@ -221,7 +196,7 @@ fn create_default_log_plugin_file(path_to_toml: &str, logs_dir: &str) -> Result< /// /// Files: /// - CONFIG_DIR/operations/c8y/c8y_LogfileRequest -/// - CONFIG_DIR/c8y/log/c8y-log-plugin.toml +/// - CONFIG_DIR/c8y/c8y-log-plugin.toml fn create_init_logs_directories_and_files( config_dir: &str, logs_dir: &str, @@ -242,24 +217,26 @@ fn create_init_logs_directories_and_files( &format!("{config_dir}/operations/c8y/c8y_LogfileRequest"), "tedge", "tedge", - 0o755, + 0o644, + None, )?; // creating c8y directory - create_directory_with_user_group(&format!("{config_dir}/c8y"), "tedge", "tedge", 0o755)?; - // creating c8y-log-plugin.toml + create_directory_with_user_group(&format!("{config_dir}/c8y"), "root", "root", 0o1777)?; - // NOTE: file needs 775 permission or inotify can not watch for changes inside the file + // creating c8y-log-plugin.toml + let logs_path = format!("{logs_dir}/tedge/agent/software-*"); + let data = toml::toml! { + files = [ + { type = "software-management", path = logs_path } + ] + }; create_file_with_user_group( &format!("{config_dir}/{DEFAULT_PLUGIN_CONFIG_FILE}"), - "tedge", - "tedge", - 0o775, + "root", + "root", + 0o644, + Some(&data.to_string()), )?; - // append default content to c8y-log-plugin.toml - create_default_log_plugin_file( - &format!("{config_dir}/{DEFAULT_PLUGIN_CONFIG_FILE}"), - logs_dir, - )?; Ok(()) } -- cgit v1.2.3