#include "config.h" #include #include #include #include #include #include "asserts.h" #include "error.h" #include "estring.h" #include "fs.h" #include "rconfig.h" #include "timer.h" #include "logger.h" #include "estat.h" #include "vaulter.h" #include "strfmt.h" #include "archiver.h" #include "table.h" #include "reporter.h" //----------------------------------------------------------------------------- /** C'tor */ vault_stats_report::vault_stats_report() { clear(); } /** C'tor */ vault_stats_report::vault_stats_report(const vault_stats_report& a_class) { clear(); assign(a_class); } /** C'tor */ vault_stats_report::vault_stats_report( const std::string& a_message, const filesystem& a_class ) { clear(); assign(a_message,a_class); } /** D'tor */ vault_stats_report::~vault_stats_report() { } /** Clear all values */ void vault_stats_report::clear(void) { m_total_blocks = 0; m_free_blocks = 0; m_total_inodes = 0; m_free_inodes = 0; } /** Assignment */ void vault_stats_report::assign( const vault_stats_report& a_class ) { assign( a_class.message(), a_class.time(), a_class.total_blocks(), a_class.free_blocks(), a_class.total_inodes(), a_class.free_inodes() ); } /** Assignment */ void vault_stats_report::assign( const std::string& a_message, const filesystem& a_class ) { assign( a_message, current_time(), a_class.total_blocks(), a_class.free_blocks(), a_class.total_inodes(), a_class.free_inodes() ); } /** Assignment */ void vault_stats_report::assign( const std::string& a_message, const std::string& a_time, const uint64 a_total_blocks, const uint64 a_free_blocks, const uint64 a_total_inodes, const uint64 a_free_inodes ) { TRY_nomem(m_message = a_message); m_total_blocks = a_total_blocks; m_free_blocks = a_free_blocks; m_total_inodes = a_total_inodes; m_free_inodes = a_free_inodes; TRY_nomem(m_time = a_time); } /** Return a string timestamp */ const std::string& vault_stats_report::time(void) const { return(m_time); } /** Return the message */ const std::string& vault_stats_report::message(void) const { return(m_message); } /** Return the total number of blocks in the vault */ const uint64 vault_stats_report::total_blocks(void) const { return(m_total_blocks); } /** Return the number of free blocks in the vault */ const uint64 vault_stats_report::free_blocks(void) const { return(m_free_blocks); } /** Return the total number of inodes in the vault */ const uint64 vault_stats_report::total_inodes(void) const { return(m_total_inodes); } /** Return the number of free inodes in the vault */ const uint64 vault_stats_report::free_inodes(void) const { return(m_free_inodes); } /** Assignment operator */ vault_stats_report& vault_stats_report::operator=(const vault_stats_report& a_class) { assign(a_class); return(*this); } //----------------------------------------------------------------------------- /** C'tor */ vault_report::vault_report() { clear(); } /** D'tor */ vault_report::~vault_report() { } /** Clear all values */ void vault_report::clear(void) { m_reports.clear(); } /** Add a vault report to the list */ void vault_report::add_report( const vault_stats_report& a_class ) { TRY_nomem(m_reports.push_back(a_class)); } /** Format and print the vault report to the given stream */ void vault_report::write_report(std::ostream& out) { table tab, tab2; estring estr; std::vector::const_iterator vi; uint64 ui64b, ui64e, ui64t; estr = "Vault Statistics"; estr.align(estring::center); tab << estr << table_endl; estr = ""; estr.fillchar('-'); estr.align(estring::left); tab << estr << table_endl; // Header tab2 << "Time" << " " << "Message" << " " << "Free Blocks" << " " << "Free Inodes" << " " << table_endl; // Separators estr.align(estring::left); estr.fillchar('-'); estr = ""; tab2 << estr << " " << estr << " " << estr << " " << estr << table_endl; // Vault messages/statistics estr.fillchar(' '); for (vi = m_reports.begin(); vi != m_reports.end(); ++vi) { tab2 << vi->time() << " "; estr.align(estring::right); estr = vi->message(); tab2 << estr; tab2 << " "; estr.align(estring::center); estr = " "; estr += percent_string(vi->free_blocks(), vi->total_blocks()); tab2 << estr; tab2 << " "; estr = " "; estr += percent_string(vi->free_inodes(), vi->total_inodes()); tab2 << estr; tab2 << table_endl; } // Separators estr.align(estring::left); estr.fillchar('-'); estr = ""; tab2 << " " << " " << " " << " " << estr << " " << estr << table_endl; // Final estr.align(estring::right); estr.fillchar(' '); estr = "Total Difference:"; // estr.align(estring::center); // estr.fillchar(' '); tab2 << " " << " " << estr << " "; estr.align(estring::center); ASSERT(m_reports.size() > 0); ui64b = m_reports[0].free_blocks(); ui64e = m_reports[m_reports.size()-1].free_blocks(); ui64t = m_reports[0].total_blocks(); if (ui64b > ui64e) { estr = "-"; estr += percent_string(ui64b - ui64e, ui64t); } else { estr = "+"; estr += percent_string(ui64e - ui64b, ui64t); } tab2 << estr; tab2 << " "; ui64b = m_reports[0].free_inodes(); ui64e = m_reports[m_reports.size()-1].free_inodes(); ui64t = m_reports[0].total_inodes(); if (ui64b > ui64e) { estr = "-"; estr += percent_string(ui64b - ui64e, ui64t); } else { estr = "+"; estr += percent_string(ui64e - ui64b, ui64t); } tab2 << estr; tab2 << table_endl; tab << tab2; out << tab; } /** Generate a synopsis report */ void vault_report::format_synopsis(table& a_table) { estring estr; estr.align(estring::right); estr = "Timestamp:"; a_table << estr << " " << config.timestamp().str() << table_endl; estr = "Vault Selected:"; a_table << estr << " " << vaulter.vault() << table_endl; } //----------------------------------------------------------------------------- /** C'tor */ job_path_report::job_path_report() { clear(); } /** C'tor */ job_path_report::job_path_report(const job_path_report& a_class) { clear(); assign(a_class); } /** C'tor */ job_path_report::job_path_report( const std::string a_source, const timer a_time, const uint16 a_exit_code, const uint16 a_signal_num, const std::string a_error_message ) { clear(); assign(a_source, a_time, a_exit_code, a_signal_num, a_error_message); } /** D'tor */ job_path_report::~job_path_report() { } /** Clear all values */ void job_path_report::clear(void) { m_source.erase(); m_time.clear(); m_exit_code = 0; m_signal_num = 0; m_error_msg.erase(); } /** Assignment */ void job_path_report::assign(const job_path_report& a_class) { assign( a_class.m_source, a_class.m_time, a_class.m_exit_code, a_class.m_signal_num, a_class.m_error_msg ); } /** Assignment */ void job_path_report::assign( const std::string a_source, const timer a_time, const uint16 a_exit_code, const uint16 a_signal_num, const std::string a_error_message ) { TRY_nomem(m_source = a_source); TRY_nomem(m_error_msg = a_error_message); TRY_nomem(m_time = a_time); m_exit_code = a_exit_code; m_signal_num = a_signal_num; } /** Return true if rsync succeeded archiving this path */ const bool job_path_report::status(void) const { if ((m_exit_code == 0) && (m_signal_num == 0)) { return(true); } return(false); } /** Set the path archived for this report */ void job_path_report::source(const std::string& a_class) { TRY_nomem(m_source = a_class); } /** Return a string of the path archived */ const std::string& job_path_report::source(void) const { return(m_source); } /** Set the timer values for this report */ void job_path_report::time(const timer& a_class) { TRY_nomem(m_time = a_class); } /** Return the timer object for this report */ const timer& job_path_report::time(void) const { return(m_time); } /** Set rsync's return exit code when archiving this path */ void job_path_report::exit_code(const int a_exit_code) { m_exit_code = a_exit_code; } /** Return rsync's exit code */ const int job_path_report::exit_code(void) const { return(m_exit_code); } /** Set rsync's signal number from archiving this path */ void job_path_report::signal_num(const int a_signal_num) { m_signal_num = a_signal_num; } /** Return rsync's signal number */ const int job_path_report::signal_num(void) const { return(m_signal_num); } /** Set a descriptive error message for this report */ void job_path_report::error_msg(const std::string& a_class) { TRY_nomem(m_error_msg = a_class); } /** Return the error message */ const std::string& job_path_report::error_msg(void) const { return(m_error_msg); } /** Assignment operator */ job_path_report& job_path_report::operator=(const job_path_report& a_class) { assign(a_class); return(*this); } //----------------------------------------------------------------------------- /** Report type tags */ const char *reportio::tags[] = { "[RSYNC]: ", "[REPORT]: ", 0 }; /** Write a report line for output from rsync to parent on child's std::cout */ void reportio::write_rsync_out(const std::string& a_str) { std::cout.flush(); std::cout << tags[rsync] << a_str << std::endl; std::cout.flush(); } /** Write a report line for output from rsync to parent on child's std::cerr */ void reportio::write_rsync_err(const std::string& a_str) { std::cerr.flush(); std::cerr << tags[rsync] << a_str << std::endl; std::cerr.flush(); } /** Generate and submit a report to the parent process on child's std::cout */ void reportio::write_report( const std::string a_source, const timer& a_timer, const int a_exit_code, const int a_signal_num, const std::string& a_error_msg ) { estring str; str += estring(a_source.size()); str += " "; str += a_source; str += " "; str += estring(static_cast(a_timer.start_value())); str += " "; str += estring(static_cast(a_timer.stop_value())); str += " "; str += estring(a_exit_code); str += " "; str += estring(a_signal_num); str += " "; str += estring(a_error_msg.size()); str += " "; str += a_error_msg; str += " "; std::cout.flush(); std::cout << tags[report] << str << std::endl; std::cout.flush(); } /** Parse a received report from a child process and return a job_path_report */ job_path_report reportio::parse(const std::string& a_str) { estring es; job_path_report jpr; estring estr; estring::size_type idx; estring::size_type size; estring tmp; estring source; timer::value_type start_time; timer::value_type stop_time; int exit_code; int signal_num; estring error_msg; TRY_nomem(estr = a_str); idx = estr.find(tags[report]); if (idx == std::string::npos) { estring es; es = "Invalid job report: \""; es += a_str; es += "\""; throw(INTERNAL_ERROR(0,es)); } // TODO: A lot of assumptions are being made here, put in some checking code estr.erase(0,idx+strlen(tags[report])); idx = estr.find(' '); tmp = estr.substr(0,idx); estr.erase(0,idx+1); size = tmp; source = estr.substr(0,size); estr.erase(0,size+1); idx = estr.find(' '); tmp = estr.substr(0,idx); estr.erase(0,idx+1); start_time = static_cast(tmp); idx = estr.find(' '); tmp = estr.substr(0,idx); estr.erase(0,idx+1); stop_time = static_cast(tmp); idx = estr.find(' '); tmp = estr.substr(0,idx); estr.erase(0,idx+1); exit_code = tmp; idx = estr.find(' '); tmp = estr.substr(0,idx); estr.erase(0,idx+1); signal_num = tmp; idx = estr.find(' '); tmp = estr.substr(0,idx); estr.erase(0,idx+1); size = tmp; error_msg = estr.substr(0,size); estr.erase(0,size+1); jpr.assign( source, timer(start_time,stop_time), exit_code, signal_num, error_msg ); return(jpr); } /** Return true if the given string looks like a valid report */ bool reportio::is_report(const std::string& a_class) { estring::size_type idx; idx = a_class.find(tags[report]); if (idx == std::string::npos) { return(false); } return(true); } //----------------------------------------------------------------------------- /** C'tor */ single_job_report::single_job_report() { clear(); } /** D'tor */ single_job_report::~single_job_report() { } /** Clear all values */ void single_job_report::clear(void) { m_reports.clear(); m_id.erase(); } /** Add a path report for this job */ void single_job_report::add_report(const job_path_report& a_class) { TRY_nomem(m_reports.push_back(a_class)); } /** Return a const vector of all path reports */ const std::vector& single_job_report::reports(void) const { return(m_reports); } /** Set a descriptive ID for this job report */ void single_job_report::id(const std::string& a_str) { TRY_nomem(m_id = a_str); } /** Return the descriptive id for this job report */ const std::string& single_job_report::id(void) const { return(m_id); } /** If all path reports say that rsync was successful, then return true, else * return false */ const bool single_job_report::status(void) const { bool value = true; std::vector::const_iterator jpri; for (jpri = m_reports.begin(); jpri != m_reports.end(); ++jpri) { if (!jpri->status()) value = false; } return(value); } //----------------------------------------------------------------------------- /** C'tor */ jobs_report::jobs_report() { clear(); } /** D'tor */ jobs_report::~jobs_report() { } /** Clear all values */ void jobs_report::clear(void) { m_jobs.clear(); } /** Add a job report to the list */ void jobs_report::add_report(const single_job_report& a_class) { TRY_nomem(m_jobs.push_back(a_class)); } /** Return a const vector of all job reports */ const std::vector& jobs_report::reports(void) const { return(m_jobs); } /** Format job reports and output to the given stream */ void jobs_report::write_report(std::ostream& a_out) { std::vector::const_iterator ji; std::vector::const_iterator jpi; estring estr; estring estr2; estring hsep; estring vsep; estring vsep2; hsep.fillchar('-'); vsep = " | "; vsep2 = "|"; for (ji = m_jobs.begin(); ji != m_jobs.end(); ++ji) { table t1, t2; bool first_line = true; if (ji != m_jobs.begin()) a_out << std::endl; estr.align(estring::left); estr = "Job: "; estr += ji->id(); t1 << estr; t1 << table_endl; t1 << hsep; t1 << table_endl; t2 << "Job" << vsep << "Path" << " " << " " << table_endl; t2 << "Status" << vsep << "Status" << " " << "Source" << table_endl; t2 << hsep << vsep << hsep << " " << hsep << table_endl; for (jpi = ji->reports().begin(); jpi != ji->reports().end(); ++jpi) { if (jpi != ji->reports().begin()) t2 << table_endl; if (first_line) { if (ji->status()) t2 << "OK"; else t2 << "ERROR"; first_line = false; } else { t2 << " " << vsep << " " << " " << table_endl << " "; } t2 << table_repeat << vsep; if (jpi->status()) t2 << "OK"; else t2 << "ERROR"; t2 << " "; t2 << jpi->source(); t2 << table_endl; t2 << " " << table_repeat << vsep << " " << " "; table t3, t4; t3 << "+" << hsep << hsep << table_endl; t3 << table_repeat << vsep2 << " "; estr.align(estring::right); estr2.align(estring::right); estr = "Start:"; estr2 = jpi->time().started_at(); estr2 += " "; t4 << " " << estr << " " << estr2 << table_endl; estr = "Finish:"; estr2 = jpi->time().stopped_at(); estr2 += " "; t4 << " " << estr << " " << estr2 << table_endl; estr = "Duration:"; estr2 = jpi->time().duration(); t4 << " " << estr << " " << estr2 << table_endl; t3 << t4 << table_endl; estr2.align(estring::left); if (!jpi->status()) { table t5; t3 << vsep2 << " " << " " << table_endl; t3 << table_repeat << vsep2 << " "; estr = "Exit Code:"; estr2 = estring(jpi->exit_code()); if (jpi->exit_code() != 0) { estr2 += " "; estr2 += rsync_estat_str.exit(jpi->exit_code()); } t5 << " " << estr << " " << estr2 << table_endl; estr = "Signal Num:"; estr2 = estring(jpi->signal_num()); if (jpi->signal_num() != 0) { estr2 += " "; estr2 += rsync_estat_str.signal(jpi->signal_num()); } t5 << " " << estr << " " << estr2 << table_endl; if (jpi->error_msg().size() > 0) { estr = "Error Msg:"; estr2 = jpi->error_msg(); t5 << " " << estr << " " << estr2 << table_endl; } t3 << t5; } // TODO: File statistics should go here t2 << t3; } t1 << t2; a_out << t1; } } /** Generate a synopsis report */ void jobs_report::format_synopsis(table& a_table) { estring estr, estr2; std::vector::const_iterator sjri; uint16 jobs_good = 0; uint16 jobs_bad = 0; uint16 jobs_total = 0; estr.align(estring::right); for (sjri = m_jobs.begin(); sjri != m_jobs.end(); ++sjri) { if (sjri->status()) ++jobs_good; else ++jobs_bad; ++jobs_total; } estr = "Successful Jobs:"; estr2 = estring(jobs_good); estr2 += " out of "; estr2 += estring(m_jobs.size()); estr2 += " ("; estr2 += percent_string(jobs_good, jobs_total); estr2 += ")"; a_table << estr << " " << estr2 << table_endl; } //----------------------------------------------------------------------------- /** C'tor */ report_manager::report_manager() { m_initialized = false; } /** D'tor */ report_manager::~report_manager() { } /** Initialize */ void report_manager::init(void) { if (!config.initialized()) { throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); } m_initialized = true; } /** Return whether or not this object has been inintialized */ const bool report_manager::initialized(void) const { return(m_initialized); } /** Clear all values */ void report_manager::clear(void) { m_total_time.clear(); m_vault.clear(); m_jobs.clear(); } /** Report the overall RVM time */ void report_manager::set_total_time(const timer& a_class) { if (!initialized()) throw(INTERNAL_ERROR(0,"Report manager is not initialized")); m_total_time = a_class; } /** Return the vault reporter object */ vault_report& report_manager::vault(void) { return(m_vault); } /** Return the jobs reporter object */ jobs_report& report_manager::jobs(void) { return(m_jobs); } /** Print report to standard output */ void report_manager::print_report(void) { estring lstr; if (!initialized()) throw(INTERNAL_ERROR(0,"Report manager is not initialized")); lstr = "Reporter - Printing report to stdout...\n"; logger.write(lstr); write_report(std::cout); lstr = "Reporter - Done\n"; logger.write(lstr); } /** Save report to a file */ void report_manager::file_report(void) { std::ofstream out; std::string filename; std::string es; estring lstr; bool done = false; int count = 0; if (!initialized()) throw(INTERNAL_ERROR(0,"Report manager is not initialized")); while (!done) { TRY_nomem(filename = config.log_dir()); TRY_nomem(filename += "/"); TRY_nomem(filename += config.timestamp().str()); TRY_nomem(filename += ".report"); if (count != 0) { TRY_nomem(filename += "."); TRY_nomem(filename += estring(count)); } if (exists(filename)) ++count; else done = true; } out.open(filename.c_str()); if (!out.is_open()) { TRY_nomem(es = "Could not open report file: \""); TRY_nomem(es += filename); TRY_nomem(es += "\""); throw(ERROR(errno,es)); } lstr = "Reporter - Writing report to file...\n"; logger.write(lstr); write_report(out); out.close(); lstr = "Reporter - Done\n"; logger.write(lstr); } /** Write report to the given stream */ void report_manager::write_report(std::ostream& a_out) { estring estr; estring es; mf_write_header(a_out); a_out << std::endl; mf_write_synopsis(a_out); a_out << std::endl; m_jobs.write_report(a_out); a_out << std::endl; m_vault.write_report(a_out); a_out << std::endl; } /** Generate a synopsis report */ void report_manager::format_synopsis(table& a_table) { estring estr; estring estr2; estr.align(estring::right); estr2.align(estring::right); estr = "Start Time:"; estr2 = m_total_time.started_at(); estr2 += " "; a_table << estr << " " << estr2 << table_endl; estr = "Finish Time:"; estr2 = m_total_time.stopped_at(); estr2 += " "; a_table << estr << " " << estr2 << table_endl; estr = "Duration:"; estr2 = m_total_time.duration(); a_table << estr << " " << estr2 << table_endl; } void report_manager::mf_write_header(std::ostream& a_out) { table t; estring estr; estr = "Rsync Vault Manager - "; estr += VERSION; t << estr << table_endl; estr = ""; estr.fillchar('='); t << estr; a_out << t; } void report_manager::mf_write_synopsis(std::ostream& a_out) { table t; estring estr; format_synopsis(t); vault().format_synopsis(t); jobs().format_synopsis(t); a_out << t; } //----------------------------------------------------------------------------- /** The global report manager */ report_manager reporter;