1 #include "config.h"
2 
3 #include <iostream>
4 #include <string>
5 #include <vector>
6 #include <algorithm>
7 #include <cassert>
8 
9 #include "asserts.h"
10 #include "error.h"
11 #include "estring.h"
12 #include "fs.h"
13 #include "rconfig.h"
14 #include "timer.h"
15 #include "logger.h"
16 #include "estat.h"
17 #include "vaulter.h"
18 #include "strfmt.h"
19 #include "archiver.h"
20 #include "table.h"
21 
22 #include "reporter.h"
23 
24 //-----------------------------------------------------------------------------
25 
26 /** C'tor */
vault_stats_report()27 vault_stats_report::vault_stats_report()
28 {
29 	clear();
30 }
31 
32 /** C'tor */
vault_stats_report(const vault_stats_report & a_class)33 vault_stats_report::vault_stats_report(const vault_stats_report& a_class)
34 {
35 	clear();
36 	assign(a_class);
37 }
38 
39 /** C'tor */
vault_stats_report(const std::string & a_message,const filesystem & a_class)40 vault_stats_report::vault_stats_report(
41 	const std::string& a_message,
42 	const filesystem& a_class
43 	)
44 {
45 	clear();
46 	assign(a_message,a_class);
47 }
48 
49 /** D'tor */
~vault_stats_report()50 vault_stats_report::~vault_stats_report()
51 {
52 }
53 
54 /** Clear all values */
clear(void)55 void vault_stats_report::clear(void)
56 {
57 	m_total_blocks = 0;
58 	m_free_blocks = 0;
59 	m_total_inodes = 0;
60 	m_free_inodes = 0;
61 }
62 
63 /** Assignment */
assign(const vault_stats_report & a_class)64 void vault_stats_report::assign(
65 	const vault_stats_report& a_class
66 	)
67 {
68 	assign(
69 		a_class.message(),
70 		a_class.time(),
71 		a_class.total_blocks(),
72 		a_class.free_blocks(),
73 		a_class.total_inodes(),
74 		a_class.free_inodes()
75 		);
76 }
77 
78 /** Assignment */
assign(const std::string & a_message,const filesystem & a_class)79 void vault_stats_report::assign(
80 	const std::string& a_message,
81 	const filesystem& a_class
82 	)
83 {
84 	assign(
85 		a_message,
86 		current_time(),
87 		a_class.total_blocks(),
88 		a_class.free_blocks(),
89 		a_class.total_inodes(),
90 		a_class.free_inodes()
91 		);
92 }
93 
94 /** Assignment */
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)95 void vault_stats_report::assign(
96 	const std::string& a_message,
97 	const std::string& a_time,
98 	const uint64 a_total_blocks,
99 	const uint64 a_free_blocks,
100 	const uint64 a_total_inodes,
101 	const uint64 a_free_inodes
102 	)
103 {
104 	TRY_nomem(m_message = a_message);
105 	m_total_blocks = a_total_blocks;
106 	m_free_blocks = a_free_blocks;
107 	m_total_inodes = a_total_inodes;
108 	m_free_inodes = a_free_inodes;
109 
110 	TRY_nomem(m_time = a_time);
111 }
112 
113 /** Return a string timestamp */
time(void) const114 const std::string& vault_stats_report::time(void) const
115 {
116 	return(m_time);
117 }
118 
119 /** Return the message */
message(void) const120 const std::string& vault_stats_report::message(void) const
121 {
122 	return(m_message);
123 }
124 
125 /** Return the total number of blocks in the vault */
total_blocks(void) const126 const uint64 vault_stats_report::total_blocks(void) const
127 {
128 	return(m_total_blocks);
129 }
130 
131 /** Return the number of free blocks in the vault */
free_blocks(void) const132 const uint64 vault_stats_report::free_blocks(void) const
133 {
134 	return(m_free_blocks);
135 }
136 
137 /** Return the total number of inodes in the vault */
total_inodes(void) const138 const uint64 vault_stats_report::total_inodes(void) const
139 {
140 	return(m_total_inodes);
141 }
142 
143 /** Return the number of free inodes in the vault */
free_inodes(void) const144 const uint64 vault_stats_report::free_inodes(void) const
145 {
146 	return(m_free_inodes);
147 }
148 
149 /** Assignment operator */
150 vault_stats_report&
operator =(const vault_stats_report & a_class)151 vault_stats_report::operator=(const vault_stats_report& a_class)
152 {
153 	assign(a_class);
154 	return(*this);
155 }
156 
157 //-----------------------------------------------------------------------------
158 
159 /** C'tor */
vault_report()160 vault_report::vault_report()
161 {
162 	clear();
163 }
164 
165 /** D'tor */
~vault_report()166 vault_report::~vault_report()
167 {
168 }
169 
170 /** Clear all values */
clear(void)171 void vault_report::clear(void)
172 {
173 	m_reports.clear();
174 }
175 
176 /** Add a vault report to the list */
add_report(const vault_stats_report & a_class)177 void vault_report::add_report(
178 	const vault_stats_report& a_class
179 	)
180 {
181 	TRY_nomem(m_reports.push_back(a_class));
182 }
183 
184 /** Format and print the vault report to the given stream */
write_report(std::ostream & out)185 void vault_report::write_report(std::ostream& out)
186 {
187 	table tab, tab2;
188 	estring estr;
189 	std::vector<vault_stats_report>::const_iterator vi;
190 	uint64 ui64b, ui64e, ui64t;
191 
192 	estr = "Vault Statistics";
193 	estr.align(estring::center);
194 	tab << estr << table_endl;
195 
196 	estr = "";
197 	estr.fillchar('-');
198 	estr.align(estring::left);
199 	tab << estr << table_endl;
200 
201 	// Header
202 	tab2
203 		<< "Time"
204 		<< " "
205 		<< "Message"
206 		<< " "
207 		<< "Free Blocks"
208 		<< " "
209 		<< "Free Inodes"
210 		<< " "
211 		<< table_endl;
212 
213 	// Separators
214 	estr.align(estring::left);
215 	estr.fillchar('-');
216 	estr = "";
217 	tab2
218 		<< estr
219 		<< " "
220 		<< estr
221 		<< " "
222 		<< estr
223 		<< " "
224 		<< estr
225 		<< table_endl;
226 
227 	// Vault messages/statistics
228 	estr.fillchar(' ');
229 	for (vi = m_reports.begin(); vi != m_reports.end(); ++vi) {
230 		tab2
231 			<< vi->time()
232 			<< " ";
233 
234 		estr.align(estring::right);
235 		estr = vi->message();
236 		tab2 << estr;
237 
238 		tab2 << " ";
239 
240 		estr.align(estring::center);
241 
242 		estr = "  ";
243 		estr += percent_string(vi->free_blocks(), vi->total_blocks());
244 		tab2 << estr;
245 
246 		tab2 << " ";
247 
248 		estr = "  ";
249 		estr += percent_string(vi->free_inodes(), vi->total_inodes());
250 		tab2 << estr;
251 
252 		tab2 << table_endl;
253 	}
254 
255 	// Separators
256 	estr.align(estring::left);
257 	estr.fillchar('-');
258 	estr = "";
259 	tab2
260 		<< " "
261 		<< " "
262 		<< " "
263 		<< " "
264 		<< estr
265 		<< " "
266 		<< estr
267 		<< table_endl;
268 
269 	// Final
270 	estr.align(estring::right);
271 	estr.fillchar(' ');
272 	estr = "Total Difference:";
273 	// estr.align(estring::center);
274 	// estr.fillchar(' ');
275 	tab2
276 		<< " "
277 		<< " "
278 		<< estr
279 		<< " ";
280 
281 	estr.align(estring::center);
282 	ASSERT(m_reports.size() > 0);
283 	ui64b = m_reports[0].free_blocks();
284 	ui64e = m_reports[m_reports.size()-1].free_blocks();
285 	ui64t = m_reports[0].total_blocks();
286 	if (ui64b > ui64e) {
287 		estr = "-";
288 		estr += percent_string(ui64b - ui64e, ui64t);
289 	}
290 	else {
291 		estr = "+";
292 		estr += percent_string(ui64e - ui64b, ui64t);
293 	}
294 	tab2 << estr;
295 
296 	tab2 << " ";
297 
298 	ui64b = m_reports[0].free_inodes();
299 	ui64e = m_reports[m_reports.size()-1].free_inodes();
300 	ui64t = m_reports[0].total_inodes();
301 	if (ui64b > ui64e) {
302 		estr = "-";
303 		estr += percent_string(ui64b - ui64e, ui64t);
304 	}
305 	else {
306 		estr = "+";
307 		estr += percent_string(ui64e - ui64b, ui64t);
308 	}
309 	tab2 << estr;
310 
311 	tab2 << table_endl;
312 
313 	tab << tab2;
314 
315 	out << tab;
316 }
317 
318 /** Generate a synopsis report */
format_synopsis(table & a_table)319 void vault_report::format_synopsis(table& a_table)
320 {
321 	estring estr;
322 
323 	estr.align(estring::right);
324 	estr = "Timestamp:";
325 	a_table << estr << " " << config.timestamp().str() << table_endl;
326 
327 	estr = "Vault Selected:";
328 	a_table << estr << " " << vaulter.vault() << table_endl;
329 }
330 
331 //-----------------------------------------------------------------------------
332 
333 /** C'tor */
job_path_report()334 job_path_report::job_path_report()
335 {
336 	clear();
337 }
338 
339 /** C'tor */
job_path_report(const job_path_report & a_class)340 job_path_report::job_path_report(const job_path_report& a_class)
341 {
342 	clear();
343 	assign(a_class);
344 }
345 
346 /** C'tor */
job_path_report(const std::string a_source,const timer a_time,const uint16 a_exit_code,const uint16 a_signal_num,const rsync_behavior::value_type a_behavior,const std::string a_error_message)347 job_path_report::job_path_report(
348 	const std::string a_source,
349 	const timer a_time,
350 	const uint16 a_exit_code,
351 	const uint16 a_signal_num,
352 	const rsync_behavior::value_type a_behavior,
353 	const std::string a_error_message
354 	)
355 {
356 	clear();
357 	assign(
358 		a_source,
359 		a_time,
360 		a_exit_code,
361 		a_signal_num,
362 		a_behavior,
363 		a_error_message
364 		);
365 }
366 
367 /** D'tor */
~job_path_report()368 job_path_report::~job_path_report()
369 {
370 }
371 
372 /** Clear all values */
clear(void)373 void job_path_report::clear(void)
374 {
375 	m_source.erase();
376 	m_time.clear();
377 	m_exit_code = 0;
378 	m_signal_num = 0;
379 	m_error_msg.erase();
380 	m_behavior = rsync_behavior::retry;
381 }
382 
383 /** Assignment */
assign(const job_path_report & a_class)384 void job_path_report::assign(const job_path_report& a_class)
385 {
386 	assign(
387 		a_class.m_source,
388 		a_class.m_time,
389 		a_class.m_exit_code,
390 		a_class.m_signal_num,
391 		a_class.m_behavior,
392 		a_class.m_error_msg
393 		);
394 }
395 
396 /** Assignment */
assign(const std::string a_source,const timer a_time,const uint16 a_exit_code,const uint16 a_signal_num,const rsync_behavior::value_type a_behavior,const std::string a_error_message)397 void job_path_report::assign(
398 	const std::string a_source,
399 	const timer a_time,
400 	const uint16 a_exit_code,
401 	const uint16 a_signal_num,
402 	const rsync_behavior::value_type a_behavior,
403 	const std::string a_error_message
404 	)
405 {
406 	TRY_nomem(m_source = a_source);
407 	TRY_nomem(m_error_msg = a_error_message);
408 	TRY_nomem(m_time = a_time);
409 	m_exit_code = a_exit_code;
410 	m_signal_num = a_signal_num;
411 	m_behavior = a_behavior;
412 }
413 
414 /** Return true if rsync succeeded archiving this path */
status(void) const415 const bool job_path_report::status(void) const
416 {
417 // std::cerr << "[DEBUG]: job_path_report::status()" << std::endl;
418 // std::cerr << "[DEBUG]:   m_source = " << m_source << std::endl;
419 // std::cerr << "[DEBUG]:   m_exit_code = " << m_exit_code << std::endl;
420 // std::cerr << "[DEBUG]:   m_signal_num = " << m_signal_num << std::endl;
421 // std::cerr << "[DEBUG]:   m_error_msg = " << m_error_msg << std::endl;
422 // std::cerr << "[DEBUG]:   m_behavior = " << m_behavior << std::endl;
423 // std::cerr << "[DEBUG]:   rsync_behavior::ok = " << rsync_behavior::ok << std::endl;
424 	if (
425 		/*
426 		((m_exit_code == 0) && (m_signal_num == 0))
427 		|| (m_behavior == rsync_behavior::ok)
428 		*/
429 		((m_exit_code == 0) || (m_behavior == rsync_behavior::ok))
430 		&& (m_signal_num == 0)
431 		) {
432 // std::cerr << "[DEBUG]:   job_path_report::status() = true" << std::endl;
433 		return(true);
434 	}
435 // std::cerr << "[DEBUG]:   job_path_report::status() = false" << std::endl;
436 	return(false);
437 }
438 
439 /** Set the path archived for this report */
source(const std::string & a_class)440 void job_path_report::source(const std::string& a_class)
441 {
442 	TRY_nomem(m_source = a_class);
443 }
444 
445 /** Return a string of the path archived */
source(void) const446 const std::string& job_path_report::source(void) const
447 {
448 	return(m_source);
449 }
450 
451 /** Set the timer values for this report */
time(const timer & a_class)452 void job_path_report::time(const timer& a_class)
453 {
454 	TRY_nomem(m_time = a_class);
455 }
456 
457 /** Return the timer object for this report */
time(void) const458 const timer& job_path_report::time(void) const
459 {
460 	return(m_time);
461 }
462 
463 /** Set rsync's return exit code when archiving this path */
exit_code(const int a_exit_code)464 void job_path_report::exit_code(const int a_exit_code)
465 {
466 	m_exit_code = a_exit_code;
467 }
468 
469 /** Return rsync's exit code */
exit_code(void) const470 const int job_path_report::exit_code(void) const
471 {
472 	return(m_exit_code);
473 }
474 
475 /** Set rsync's signal number from archiving this path */
signal_num(const int a_signal_num)476 void job_path_report::signal_num(const int a_signal_num)
477 {
478 	m_signal_num = a_signal_num;
479 }
480 
481 /** Return rsync's signal number */
signal_num(void) const482 const int job_path_report::signal_num(void) const
483 {
484 	return(m_signal_num);
485 }
486 
487 /** Set a descriptive error message for this report */
error_msg(const std::string & a_class)488 void job_path_report::error_msg(const std::string& a_class)
489 {
490 	TRY_nomem(m_error_msg = a_class);
491 }
492 
493 /** Return the error message */
error_msg(void) const494 const std::string& job_path_report::error_msg(void) const
495 {
496 	return(m_error_msg);
497 }
498 
499 /** Assignment operator */
operator =(const job_path_report & a_class)500 job_path_report& job_path_report::operator=(const job_path_report& a_class)
501 {
502 	assign(a_class);
503 	return(*this);
504 }
505 
506 //-----------------------------------------------------------------------------
507 
508 /** Report type tags */
509 const char *reportio::tags[] = {
510 	"[RSYNC]: ",
511 	"[REPORT]: ",
512 	0
513 };
514 
515 /** Write a report line for output from rsync to parent on child's std::cout
516  */
write_rsync_out(const std::string & a_str)517 void reportio::write_rsync_out(const std::string& a_str)
518 {
519 	std::cout.flush();
520 	std::cout << tags[rsync] << a_str << std::endl;
521 	std::cout.flush();
522 }
523 
524 /** Write a report line for output from rsync to parent on child's std::cerr
525  */
write_rsync_err(const std::string & a_str)526 void reportio::write_rsync_err(const std::string& a_str)
527 {
528 	std::cerr.flush();
529 	std::cerr << tags[rsync] << a_str << std::endl;
530 	std::cerr.flush();
531 }
532 
533 /** Generate and submit a report to the parent process on child's std::cout
534  */
write_report(const std::string a_source,const timer & a_timer,const int a_exit_code,const int a_signal_num,const rsync_behavior::value_type & a_behavior,const std::string & a_error_msg)535 void reportio::write_report(
536 		const std::string a_source,
537 		const timer& a_timer,
538 		const int a_exit_code,
539 		const int a_signal_num,
540 		const rsync_behavior::value_type& a_behavior,
541 		const std::string& a_error_msg
542 	)
543 {
544 	estring str;
545 
546 	str += estring(a_source.size());
547 	str += " ";
548 	str += a_source;
549 	str += " ";
550 	str += estring(static_cast<uint64>(a_timer.start_value()));
551 	str += " ";
552 	str += estring(static_cast<uint64>(a_timer.stop_value()));
553 	str += " ";
554 	str += estring(a_exit_code);
555 	str += " ";
556 	str += estring(a_signal_num);
557 	str += " ";
558 	str += estring(static_cast<uint64>(a_behavior));
559 	str += " ";
560 	str += estring(a_error_msg.size());
561 	str += " ";
562 	str += a_error_msg;
563 	str += " ";
564 
565 	std::cout.flush();
566 	std::cout << tags[report] << str << std::endl;
567 	std::cout.flush();
568 }
569 
570 /** Parse a received report from a child process and return a job_path_report
571  */
parse(const std::string & a_str)572 job_path_report reportio::parse(const std::string& a_str)
573 {
574 	estring es;
575 	job_path_report jpr;
576 	estring estr;
577 	estring::size_type idx;
578 	estring::size_type size;
579 	estring tmp;
580 	estring source;
581 	timer::value_type start_time;
582 	timer::value_type stop_time;
583 	int exit_code;
584 	int signal_num;
585 	estring error_msg;
586 	rsync_behavior::behavior_type behavior;
587 
588 	TRY_nomem(estr = a_str);
589 	idx = estr.find(tags[report]);
590 	if (idx == std::string::npos) {
591 		estring es;
592 
593 		es = "Invalid job report: \"";
594 		es += a_str;
595 		es += "\"";
596 
597 		throw(INTERNAL_ERROR(0,es));
598 	}
599 
600 	// TODO: A lot of assumptions are being made here, put in some checking code
601 
602 	estr.erase(0,idx+strlen(tags[report]));
603 
604 	idx = estr.find(' ');
605 	tmp = estr.substr(0,idx);
606 	estr.erase(0,idx+1);
607 	size = tmp;
608 	source = estr.substr(0,size);
609 	estr.erase(0,size+1);
610 
611 	idx = estr.find(' ');
612 	tmp = estr.substr(0,idx);
613 	estr.erase(0,idx+1);
614 	start_time = static_cast<uint64>(tmp);
615 
616 	idx = estr.find(' ');
617 	tmp = estr.substr(0,idx);
618 	estr.erase(0,idx+1);
619 	stop_time = static_cast<uint64>(tmp);
620 
621 	idx = estr.find(' ');
622 	tmp = estr.substr(0,idx);
623 	estr.erase(0,idx+1);
624 	exit_code = tmp;
625 
626 	idx = estr.find(' ');
627 	tmp = estr.substr(0,idx);
628 	estr.erase(0,idx+1);
629 	signal_num = tmp;
630 
631 	idx = estr.find(' ');
632 	tmp = estr.substr(0,idx);
633 	estr.erase(0,idx+1);
634 	behavior =
635 		static_cast<rsync_behavior::value_type>(
636 			static_cast<uint64>(tmp)
637 			);
638 
639 	idx = estr.find(' ');
640 	tmp = estr.substr(0,idx);
641 	estr.erase(0,idx+1);
642 	size = tmp;
643 	error_msg = estr.substr(0,size);
644 	estr.erase(0,size+1);
645 
646 	jpr.assign(
647 		source,
648 		timer(start_time,stop_time),
649 		exit_code,
650 		signal_num,
651 		behavior,
652 		error_msg
653 	);
654 
655 	return(jpr);
656 }
657 
658 /** Return true if the given string looks like a valid report */
is_report(const std::string & a_class)659 bool reportio::is_report(const std::string& a_class)
660 {
661 	estring::size_type idx;
662 
663 	idx = a_class.find(tags[report]);
664 	if (idx == std::string::npos) {
665 		return(false);
666 	}
667 	return(true);
668 }
669 
670 //-----------------------------------------------------------------------------
671 
672 /** C'tor */
single_job_report()673 single_job_report::single_job_report()
674 {
675 	clear();
676 }
677 
678 /** D'tor */
~single_job_report()679 single_job_report::~single_job_report()
680 {
681 }
682 
683 /** Clear all values  */
clear(void)684 void single_job_report::clear(void)
685 {
686 	m_reports.clear();
687 	m_id.erase();
688 }
689 
690 /** Add a path report for this job */
add_report(const job_path_report & a_class)691 void single_job_report::add_report(const job_path_report& a_class)
692 {
693 	TRY_nomem(m_reports.push_back(a_class));
694 }
695 
696 /** Return a const vector of all path reports */
reports(void) const697 const std::vector<job_path_report>& single_job_report::reports(void) const
698 {
699 	return(m_reports);
700 }
701 
702 /** Set a descriptive ID for this job report */
id(const std::string & a_str)703 void single_job_report::id(const std::string& a_str)
704 {
705 	TRY_nomem(m_id = a_str);
706 }
707 
708 /** Return the descriptive id for this job report */
id(void) const709 const std::string& single_job_report::id(void) const
710 {
711 	return(m_id);
712 }
713 
714 /** If all path reports say that rsync was successful, then return true, else
715  * return false */
status(void) const716 const bool single_job_report::status(void) const
717 {
718 	bool value = true;
719 	std::vector<job_path_report>::const_iterator jpri;
720 
721 	for (jpri = m_reports.begin(); jpri != m_reports.end(); ++jpri) {
722 		if (!jpri->status())
723 			value = false;
724 	}
725 
726 	return(value);
727 }
728 
729 //-----------------------------------------------------------------------------
730 
731 /** C'tor */
jobs_report()732 jobs_report::jobs_report()
733 {
734 	clear();
735 }
736 
737 /** D'tor */
~jobs_report()738 jobs_report::~jobs_report()
739 {
740 }
741 
742 /** Clear all values */
clear(void)743 void jobs_report::clear(void)
744 {
745 	m_jobs.clear();
746 }
747 
748 /** Add a job report to the list */
add_report(const single_job_report & a_class)749 void jobs_report::add_report(const single_job_report& a_class)
750 {
751 	TRY_nomem(m_jobs.push_back(a_class));
752 }
753 
754 /** Return a const vector of all job reports */
reports(void) const755 const std::vector<single_job_report>& jobs_report::reports(void) const
756 {
757 	return(m_jobs);
758 }
759 
760 /** Format job reports and output to the given stream */
write_report(std::ostream & a_out)761 void jobs_report::write_report(std::ostream& a_out)
762 {
763 	std::vector<single_job_report>::const_iterator ji;
764 	std::vector<job_path_report>::const_iterator jpi;
765 	estring estr;
766 	estring estr2;
767 	estring hsep;
768 	estring vsep;
769 	estring vsep2;
770 
771 	hsep.fillchar('-');
772 	vsep = " | ";
773 	vsep2 = "|";
774 
775 	for (ji = m_jobs.begin(); ji != m_jobs.end(); ++ji) {
776 		table t1, t2;
777 		bool first_line = true;
778 
779 		if (ji != m_jobs.begin())
780 			a_out << std::endl;
781 
782 		estr.align(estring::left);
783 		estr = "Job: ";
784 		estr += ji->id();
785 		t1 << estr;
786 		t1 << table_endl;
787 
788 		t1 << hsep;
789 		t1 << table_endl;
790 
791 		t2 << "Job" << vsep << "Path" << " " << " "
792 			<< table_endl;
793 		t2 << "Status" << vsep << "Status" << " " << "Source"
794 			<< table_endl;
795 		t2 << hsep << vsep << hsep << " " << hsep << table_endl;
796 
797 		for (jpi = ji->reports().begin(); jpi != ji->reports().end(); ++jpi) {
798 			if (jpi != ji->reports().begin())
799 				t2 << table_endl;
800 
801 			if (first_line) {
802 				if (ji->status())
803 					t2 << "OK";
804 				else
805 					t2 << "ERROR";
806 				first_line = false;
807 			}
808 			else {
809 				t2 << " " << vsep << " " << " " << table_endl << " ";
810 			}
811 
812 			t2 << table_repeat << vsep;
813 
814 			if (jpi->status())
815 				t2 << "OK";
816 			else
817 				t2 << "ERROR";
818 
819 			t2 << " ";
820 
821 			t2 << jpi->source();
822 
823 			t2 << table_endl;
824 
825 			t2 << " " << table_repeat << vsep << " " << " ";
826 
827 			table t3, t4;
828 
829 			t3 << "+" << hsep << hsep << table_endl;
830 			t3 << table_repeat << vsep2 << " ";
831 
832 			estr.align(estring::right);
833 			estr2.align(estring::right);
834 
835 			estr = "Start:";
836 			estr2 = jpi->time().started_at();
837 			estr2 += "  ";
838 			t4 << " " << estr << " " << estr2 << table_endl;
839 
840 			estr = "Finish:";
841 			estr2 = jpi->time().stopped_at();
842 			estr2 += "  ";
843 			t4 << " " << estr << " " << estr2 << table_endl;
844 
845 			estr = "Duration:";
846 			estr2 = jpi->time().duration();
847 			t4 << " " << estr << " " << estr2 << table_endl;
848 
849 			t3 << t4 << table_endl;
850 
851 			estr2.align(estring::left);
852 			if (!jpi->status()) {
853 				table t5;
854 
855 				t3 << vsep2 << " " << " " << table_endl;
856 				t3 << table_repeat << vsep2 << " ";
857 
858 				estr = "Exit Code:";
859 				estr2 = estring(jpi->exit_code());
860 				if (jpi->exit_code() != 0) {
861 					estr2 += " ";
862 					estr2 += rsync_estat_str.exit(jpi->exit_code());
863 				}
864 				t5 << " " << estr << " " << estr2 << table_endl;
865 
866 				estr = "Signal Num:";
867 				estr2 = estring(jpi->signal_num());
868 				if (jpi->signal_num() != 0) {
869 					estr2 += " ";
870 					estr2 += rsync_estat_str.signal(jpi->signal_num());
871 				}
872 				t5 << " " << estr << " " << estr2 << table_endl;
873 
874 				if (jpi->error_msg().size() > 0) {
875 					estr = "Error Msg:";
876 					estr2 = jpi->error_msg();
877 					t5 << " " << estr << " " << estr2 << table_endl;
878 				}
879 
880 				t3 << t5;
881 			}
882 
883 			// TODO: File statistics should go here
884 
885 			t2 << t3;
886 		}
887 
888 		t1 << t2;
889 
890 		a_out << t1;
891 	}
892 }
893 
894 /** Generate a synopsis report */
format_synopsis(table & a_table)895 void jobs_report::format_synopsis(table& a_table)
896 {
897 	estring estr, estr2;
898 	std::vector<single_job_report>::const_iterator sjri;
899 	uint16 jobs_good = 0;
900 	uint16 jobs_bad = 0;
901 	uint16 jobs_total = 0;
902 
903 	estr.align(estring::right);
904 
905 	for (sjri = m_jobs.begin(); sjri != m_jobs.end(); ++sjri) {
906 		if (sjri->status())
907 			++jobs_good;
908 		else
909 			++jobs_bad;
910 		++jobs_total;
911 	}
912 
913 	estr = "Successful Jobs:";
914 	estr2 = estring(jobs_good);
915 	estr2 += " out of ";
916 	estr2 += estring(m_jobs.size());
917 	estr2 += " (";
918 	estr2 += percent_string(jobs_good, jobs_total);
919 	estr2 += ")";
920 
921 	a_table << estr << " " << estr2 << table_endl;
922 }
923 
924 //-----------------------------------------------------------------------------
925 
926 /** C'tor */
report_manager()927 report_manager::report_manager()
928 {
929 	m_initialized = false;
930 }
931 
932 /** D'tor */
~report_manager()933 report_manager::~report_manager()
934 {
935 }
936 
937 /** Initialize */
init(void)938 void report_manager::init(void)
939 {
940 	if (!config.initialized()) {
941 		throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
942 	}
943 	m_initialized = true;
944 }
945 
946 /** Return whether or not this object has been inintialized */
initialized(void) const947 const bool report_manager::initialized(void) const
948 {
949 	return(m_initialized);
950 }
951 
952 /** Clear all values */
clear(void)953 void report_manager::clear(void)
954 {
955 	m_total_time.clear();
956 	m_vault.clear();
957 	m_jobs.clear();
958 }
959 
960 /** Report the overall RVM time */
set_total_time(const timer & a_class)961 void report_manager::set_total_time(const timer& a_class)
962 {
963 	if (!initialized())
964 		throw(INTERNAL_ERROR(0,"Report manager is not initialized"));
965 
966 	m_total_time = a_class;
967 }
968 
969 /** Return the vault reporter object */
vault(void)970 vault_report& report_manager::vault(void)
971 {
972 	return(m_vault);
973 }
974 
975 /** Return the jobs reporter object */
jobs(void)976 jobs_report& report_manager::jobs(void)
977 {
978 	return(m_jobs);
979 }
980 
981 /** Print report to standard output */
print_report(void)982 void report_manager::print_report(void)
983 {
984 	estring lstr;
985 
986 	if (!initialized())
987 		throw(INTERNAL_ERROR(0,"Report manager is not initialized"));
988 
989 	lstr = "Reporter - Printing report to stdout...\n";
990 	logger.write(lstr);
991 
992 	write_report(std::cout);
993 
994 	lstr = "Reporter - Done\n";
995 	logger.write(lstr);
996 }
997 
998 /** Save report to a file */
file_report(void)999 void report_manager::file_report(void)
1000 {
1001 	std::ofstream out;
1002 	std::string filename;
1003 	std::string es;
1004 	estring lstr;
1005 	bool done = false;
1006 	int count = 0;
1007 
1008 	if (!initialized())
1009 		throw(INTERNAL_ERROR(0,"Report manager is not initialized"));
1010 
1011 	while (!done) {
1012 		TRY_nomem(filename = config.log_dir());
1013 		TRY_nomem(filename += "/");
1014 		TRY_nomem(filename += config.timestamp().str());
1015 		TRY_nomem(filename += ".report");
1016 		if (count != 0) {
1017 			TRY_nomem(filename += ".");
1018 			TRY_nomem(filename += estring(count));
1019 		}
1020 		if (exists(filename))
1021 			++count;
1022 		else
1023 			done = true;
1024 	}
1025 	out.open(filename.c_str());
1026 	if (!out.is_open()) {
1027 		TRY_nomem(es = "Could not open report file: \"");
1028 		TRY_nomem(es += filename);
1029 		TRY_nomem(es += "\"");
1030 		throw(ERROR(errno,es));
1031 	}
1032 
1033 	lstr = "Reporter - Writing report to file...\n";
1034 	logger.write(lstr);
1035 	write_report(out);
1036 
1037 	out.close();
1038 
1039 	lstr = "Reporter - Done\n";
1040 	logger.write(lstr);
1041 }
1042 
1043 /** Write report to the given stream */
write_report(std::ostream & a_out)1044 void report_manager::write_report(std::ostream& a_out)
1045 {
1046 	estring estr;
1047 	estring es;
1048 
1049 	mf_write_header(a_out);
1050 
1051 	a_out << std::endl;
1052 
1053 	mf_write_synopsis(a_out);
1054 
1055 	a_out << std::endl;
1056 
1057 	m_jobs.write_report(a_out);
1058 
1059 	a_out << std::endl;
1060 
1061 	m_vault.write_report(a_out);
1062 
1063 	a_out << std::endl;
1064 }
1065 
1066 /** Generate a synopsis report */
format_synopsis(table & a_table)1067 void report_manager::format_synopsis(table& a_table)
1068 {
1069 	estring estr;
1070 	estring estr2;
1071 
1072 	estr.align(estring::right);
1073 	estr2.align(estring::right);
1074 
1075 	estr = "Start Time:";
1076 	estr2 = m_total_time.started_at();
1077 	estr2 += "  ";
1078 
1079 	a_table << estr << " " << estr2 << table_endl;
1080 
1081 	estr = "Finish Time:";
1082 	estr2 = m_total_time.stopped_at();
1083 	estr2 += "  ";
1084 
1085 	a_table << estr << " " << estr2 << table_endl;
1086 
1087 	estr = "Duration:";
1088 	estr2 = m_total_time.duration();
1089 
1090 	a_table << estr << " " << estr2 << table_endl;
1091 }
1092 
mf_write_header(std::ostream & a_out)1093 void report_manager::mf_write_header(std::ostream& a_out)
1094 {
1095 	table t;
1096 	estring estr;
1097 
1098 	estr = "Rsync Vault Manager - ";
1099 	estr += VERSION;
1100 
1101 	t << estr << table_endl;
1102 
1103 	estr = "";
1104 	estr.fillchar('=');
1105 	t << estr;
1106 
1107 	a_out << t;
1108 }
1109 
mf_write_synopsis(std::ostream & a_out)1110 void report_manager::mf_write_synopsis(std::ostream& a_out)
1111 {
1112 	table t;
1113 	estring estr;
1114 
1115 	format_synopsis(t);
1116 	vault().format_synopsis(t);
1117 	jobs().format_synopsis(t);
1118 
1119 	a_out << t;
1120 }
1121 
1122 //-----------------------------------------------------------------------------
1123 
1124 /** The global report manager */
1125 report_manager reporter;
1126 
1127