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