use itertools::Itertools;
use log::{debug, error, info, trace, warn, log_enabled};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::{Command, Output};
use whoami::username;
///////////////////////////////////////////////////////////////////////
//
/*
my $VERBOSE = q{-v};
# mount->cmd needs T_FSTYPE and T_UUID
$cmds->{'mount'}->{'cmd'} = $cmds->{'mount'}->{'bin'} . qq{ $VERBOSE -t $T_FSTYPE -U $T_UUID $FS_MOUNTPOINT } . $cmds->{'mount'}->{'opt'};
# rsync-opt needs excludes filename, derived from T_UUID
my $CHKSUM = q{};
if ($RSYNC_CHECKSUMS) {
INFO(q{Checksums for rsync are enabled});
$CHKSUM = q{-c}
};
$cmds->{'rsync'}->{'opt'} = qq{-a $VERBOSE $CHKSUM --delete --delete-excluded --delete-before }
. qq{--exclude-from=$backup_vols->{$T_UUID}->{'EXCLUDES'} };
$cmds->{'rsync'}->{'cmd'} = qq{$cmds->{'ionice'}->{'bin'} $cmds->{'ionice'}->{'opt'} }
. qq{$cmds->{'rsync'}->{'bin'} $cmds->{'rsync'}->{'opt'} }
. qq{$backup_vols->{$T_UUID}->{'SOURCES'} $FS_MOUNTPOINT};
*/
//
//
/*
$cmds->{'rsync'}->{'cmd'} = qq{$cmds->{'ionice'}->{'bin'} $cmds->{'ionice'}->{'opt'} }
. qq{$cmds->{'rsync'}->{'bin'}
. qq{-a $VERBOSE $CHKSUM --delete --delete-excluded --delete-before }
. qq{--exclude-from=$backup_vols->{$T_UUID}->{'EXCLUDES'} };
. qq{$backup_vols->{$T_UUID}->{'SOURCES'} $FS_MOUNTPOINT};
'SOURCES' => '/boot /etc /home /opt /root /srv /storage /usr /var'
*/
///////////////////////////////////////////////////////////////////////
fn test_rsync() {
info!("test_rsync() - Begin.");
info!("testing rsync with rpm exclusion file...");
let mut command = Command::new("/usr/bin/ionice");
command.arg("-c"); // scheduling class
command.arg("3"); // idle
command.arg("/usr/bin/rsync");
command.arg("--dry-run");
command.arg("-a"); // archive mode
command.arg("-v"); // verbose
// command.arg(""); // checksum
command.arg("--delete");
command.arg("--delete-excluded");
command.arg("--delete-before");
// command.arg("--exclude-from=rsync-excludes.txt"); // RPM EXCLUDES
command.arg("--exclude-from=excludes-1tb.conf"); // VOLUME EXCLUDES
command.arg("/boot"); // SOURCES
command.arg("/etc"); // SOURCES
command.arg("/home"); // SOURCES
command.arg("/opt"); // SOURCES
command.arg("/root"); // SOURCES
command.arg("/srv"); // SOURCES
command.arg("/storage"); // SOURCES
command.arg("/usr"); // SOURCES
command.arg("/var"); // SOURCES
command.arg("/storage/tmp/rsync-test"); // FS_MOUNTPOINT
let args: Vec<&OsStr> = command.get_args().collect();
info!("Arguments to ionice: {:?}", args);
match command.output() {
Ok(o) => {
// stdout
match String::from_utf8(o.stdout) {
Ok(stdout) => {
for line in stdout.lines() {
info!("stdout: {}", line);
} // for
}, // Ok(stdout)
Err(e) => {
error!("Could not convert from UTF8. Bailing out! {:?}", e);
panic!("Could not convert from UTF8. Bailing out! {:?}", e);
}, // Err
} //match stdout
// stderr
match String::from_utf8(o.stderr) {
Ok(stderr) => {
for line in stderr.lines() {
info!("stderr: {}", line);
} // for
}, // Ok(stderr)
Err(e) => {
error!("Could not convert from UTF8. Bailing out! {:?}", e);
panic!("Could not convert from UTF8. Bailing out! {:?}", e);
}, // Err
} //match stderr
}, // Ok(o)
Err(e) => {
error!("Could not run the command. Bailing out! {:?}", e);
panic!("Could not run the command. Bailing out! {:?}", e);
}, // Err
} // match
info!("test_rsync() - End.");
} // fn test_rsync
///////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////
fn main() {
match setup_logger() {
Ok(_) => info!("Logging initialized. (log level: {})", log::max_level()),
Err(e) => {
println!("Could not initialize logging: {e:?}. Bailing out! ");
std::process::exit(1);
},
} // match setup_logger
let username = username();
info!("Running as user '{}'", username);
if username.ne(&String::from("root")) {
error!("YOU ARE NOT RUNNING AS 'root'! YOU WILL NOT GET A PROPER BACKUP!");
}
debug!("create_rsync_exclude_rpm_files()");
create_rsync_exclude_rpm_files();
info!("testing rsync w/ exclude file (--dry-run)");
test_rsync();
} // main
///////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////
fn setup_logger() -> Result<(), fern::InitError> {
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{} [{}] [{}] {}",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
record.target(),
record.level(),
message
))
})
.level(log::LevelFilter::Debug)
.chain(std::io::stdout())
.chain(fern::log_file("output.log")?)
.apply()?;
Ok(())
} //setup_logger
///////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////
fn get_all_rpm_files(f: &mut HashMap<String, i32>) {
info!("Collecting exclusion list of files from all installed rpm packages...");
let mut command = Command::new("/usr/bin/rpm");
command.arg("-qal");
match command.output() {
Ok(output) => {
process_rpm_file(f, output);
},
Err(e) => {
error!("Could not run the command. Bailing out! {:?}", e);
panic!("Could not run the command. Bailing out! {:?}", e);
},
} // match command.output()
info!("HashMap has {} elements.", f.len());
} // get_all_rpm_files
///////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////
fn process_rpm_file(f: &mut HashMap<String, i32>, o: Output) {
// stdout
match String::from_utf8(o.stdout) {
Ok(stdout) => {
for line in stdout.lines() {
trace!("{}", line);
if Path::new(line).is_file() {
trace!("{} is a regular file", line);
// insert file, with some arbitrary value
f.insert( line.to_string(), 1 ) ;
} // if path exists
else {
debug!("{} is not a regular file. Skipped.", line);
} // else
} // for
}, // Ok(stdout)
Err(e) => {
error!("Could not convert from UTF8. Bailing out! {:?}", e);
panic!("Could not convert from UTF8. Bailing out! {:?}", e);
}, // Err
} //match
// stderr
match String::from_utf8(o.stderr) {
Ok(stderr) => {
for line in stderr.lines() {
info!("stderr: {}", line);
} // for
}, // Ok(stderr)
Err(e) => {
error!("Could not convert from UTF8. Bailing out! {:?}", e);
panic!("Could not convert from UTF8. Bailing out! {:?}", e);
}, // Err
} //match stderr
} // fn process_rom_file
///////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////
fn get_rpm_verify_info(f: &mut HashMap<String, i32>) {
info!("Verifying files on exclusion list...");
let mut command = Command::new("/usr/bin/ionice");
command.arg("-c"); // scheduling class
command.arg("3"); // idle
command.arg("/usr/bin/rpm");
command.arg("-Va"); // Verify all
match command.output() {
Ok(output) => {
process_verify_entry(f, output);
},
Err(e) => {
error!("Could not run the command. Bailing out! {:?}", e);
panic!("Could not run the command. Bailing out! {:?}", e);
},
} // match command.output()
info!("HashMap has {} elements.", f.len());
} // get_rpm_verify_info
///////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////
fn process_verify_entry(f: &mut HashMap<String, i32>, o: Output) {
// stdout
match String::from_utf8(o.stdout) {
Ok(stdout) => {
let mut file_list: Vec<&str> = Vec::new();
for line in stdout.lines() {
trace!("{}", line);
if line.contains("(Permission denied)") {
error!("{}", line);
} // if line contains...
else {
let mut t: Vec<&str> = line.split_whitespace().collect();
t.reverse();
let _flags = t.pop().unwrap();
t.reverse();
let name = t.pop().unwrap();
if Path::new(name).is_file() {
file_list.push(name);
}
else {
debug!("{} is a directory",name);
}
} // else line contains.
} // for
file_list.sort();
file_list.dedup();
info!("Removing changed files from exclusion list...");
let mut removed_count = 0;
for elem in file_list.iter() {
f.remove(&elem.to_string());
debug!("removed {} from HashMap", elem);
removed_count += 1;
trace!("HashMap has {} elements.", f.len());
} // for...
info!("Total removed from HashMap: {}", removed_count);
}, // Ok(stdout)
Err(e) => {
error!("Could not convert from UTF8. Bailing out! {:?}", e);
panic!("Could not convert from UTF8. Bailing out! {:?}", e);
}, // Err
} //match
// stderr
match String::from_utf8(o.stderr) {
Ok(stderr) => {
for line in stderr.lines() {
info!("stderr: {}", line);
} // for
}, // Ok(stderr)
Err(e) => {
error!("Could not convert from UTF8. Bailing out! {:?}", e);
panic!("Could not convert from UTF8. Bailing out! {:?}", e);
}, // Err
} //match stderr
} // fn process_verify_entry
///////////////////////////////////////////////////////////////////////
//
// fn create_rsync_exclude_rpm_files()
//
// Write HashMap keys to rsync-format exclude file
//
///////////////////////////////////////////////////////////////////////
fn create_rsync_exclude_rpm_files() {
info!("Creating rsync exclude file for unchanged rpm managed files...");
let mut files: HashMap<String, i32> = HashMap::new();
info!("HashMap has {} elements.", files.len());
// step 1
get_all_rpm_files(&mut files);
trace!("HashMap has {} elements after get_all_rpm_files.", files.len());
if log_enabled!(log::Level::Trace) {
trace!("Raw {:?}", files);
trace!("Contents of HashMap");
for (file, status) in &files {
trace!("[all-files-tag] {}: {}", file, status);
}
} // if log enabled
// step 2
get_rpm_verify_info(&mut files);
trace!("HashMap has {} elements after get_rpm_verify_info.", files.len());
if log_enabled!(log::Level::Trace) {
trace!("Raw {:?}", files);
trace!("Contents of HashMap");
for (file, status) in &files {
trace!("[all-rpm-tag] {}: {}", file, status);
}
} // if log enabled
// step 3
let mut exclude_file = File::create("rsync-excludes.txt").expect("Unable to create file");
for file in files.keys().sorted() {
let line = format!("{}\n", file);
trace!("{}", line);
exclude_file.write_all(line.as_bytes()).expect("Unable to write data");
} // for...
} // fn create_rsync_exclude_rpm_files()
Comments
0 B
|👍
/👎
0 B
|👍
/👎