2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998, 2000 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
28 * $Id: reporter.c,v 1.132 2006/08/28 17:02:48 martinea Exp $
30 * nightly Amanda Report generator
53 #include "timestamp.h"
56 /* don't have (or need) a skipped type except internally to reporter */
57 #define L_SKIPPED L_MARKER
60 #define STATUS_STRANGE 2
61 #define STATUS_FAILED 4
62 #define STATUS_MISSING 8
63 #define STATUS_TAPE 16
65 typedef struct line_s {
66 struct line_s *next, *last;
70 typedef struct timedata_s {
72 double origsize, outsize;
80 typedef struct repdata_s {
83 double est_nsize, est_csize;
89 struct repdata_s *next;
92 #define data(dp) ((repdata_t *)(dp)->up)
94 static struct cumulative_stats {
95 int dumpdisks, tapedisks, tapechunks;
96 double taper_time, dumper_time;
97 double outsize, origsize, tapesize;
98 double coutsize, corigsize; /* compressed dump only */
101 static int dumpdisks[10], tapedisks[10], tapechunks[10]; /* by-level breakdown of disk count */
103 typedef struct taper_s {
106 double coutsize, corigsize;
107 int tapedisks, tapechunks;
108 struct taper_s *next;
111 static taper_t *stats_by_tape = NULL;
112 static taper_t *current_tape = NULL;
114 typedef struct X_summary_s {
119 struct X_summary_s *next;
122 static X_summary_t *first_strange=NULL, *last_strange=NULL;
123 static X_summary_t *first_failed=NULL, *last_failed=NULL;
125 static double total_time, startup_time, planner_time;
127 /* count files to tape */
128 static int tapefcount = 0;
130 static int exit_status = 0;
131 static char *run_datestamp;
132 static char *tape_labels = NULL;
133 static int last_run_tapes = 0;
134 static int degraded_mode = 0; /* defined in driverio too */
135 static int normal_run = 0;
136 static int amflush_run = 0;
137 static int got_finish = 0;
138 static int cmdlogfname = 0;
139 static char *ghostname = NULL;
141 static char *tapestart_error = NULL;
143 static FILE *logfile, *mailf;
145 static FILE *postscript;
146 static char *printer;
148 static disklist_t diskq;
149 static disklist_t sortq;
151 static line_t *errsum = NULL;
152 static line_t *errdet = NULL;
153 static line_t *strangedet = NULL;
154 static line_t *notes = NULL;
156 static char MaxWidthsRequested = 0; /* determined via config data */
158 static char *displayunit;
159 static long int unitdivisor;
161 /* local functions */
162 int main(int argc, char **argv);
164 static char * nicedate(const char * datestamp);
165 static char * prefix(char *host, char *disk, int level);
166 static char * prefixstrange(char *host, char *disk, int level,
167 size_t len_host, size_t len_disk);
168 static char * Rule(int From, int To);
169 static char * sDivZero(double a, double b, int cn);
170 static char * TextRule(int From, int To, char *s);
171 static int ColWidth(int From, int To);
172 static int contline_next(void);
173 static int sort_by_name(disk_t *a, disk_t *b);
174 static repdata_t *find_repdata(disk_t *dp, char *datestamp, int level);
175 static repdata_t *handle_chunk(logtype_t logtype);
176 static repdata_t *handle_success(logtype_t logtype);
177 static void addline(line_t **lp, char *str);
178 static void addtoX_summary(X_summary_t **first, X_summary_t **last,
179 char *host, char *disk, int level, char *str);
180 static void bogus_line(const char *);
181 static void CalcMaxWidth(void);
182 static void CheckFloatMax(ColumnInfo *cd, double d);
183 static void CheckIntMax(ColumnInfo *cd, int n);
184 static void CheckStringMax(ColumnInfo *cd, char *s);
185 static void copy_template_file(char *lbl_templ);
186 static void do_postscript_output(void);
187 static void generate_missing(void);
188 static void generate_bad_estimate(void);
189 static void handle_disk(void);
190 static void handle_error(void);
191 static void handle_failed(void);
192 static void handle_finish(void);
193 static void handle_note(void);
194 static void handle_partial(void);
195 static void handle_start(void);
196 static void handle_stats(void);
197 static void handle_strange(void);
198 static void handle_summary(void);
199 static void output_lines(line_t *lp, FILE *f);
200 static void output_stats(void);
201 static void output_X_summary(X_summary_t *first);
202 static void output_summary(void);
203 static void output_tapeinfo(void);
204 static void sort_disks(void);
205 static void usage(void);
213 for (i=From; i<=To && ColumnData[i].Name != NULL; i++) {
214 Width+= ColumnData[i].PrefixSpace + ColumnData[i].Width;
225 int Leng= ColWidth(0, ColumnDataCount());
226 char *RuleSpace= alloc((size_t)(Leng+1));
227 ThisLeng= ColWidth(From, To);
228 for (i=0;i<ColumnData[From].PrefixSpace; i++)
230 for (; i<ThisLeng; i++)
232 RuleSpace[ThisLeng]= '\0';
242 ColumnInfo *cd= &ColumnData[From];
244 int nbrules, i, txtlength;
245 int RuleSpaceSize= ColWidth(0, ColumnDataCount());
246 char *RuleSpace= alloc((size_t)RuleSpaceSize), *tmp;
248 leng = (int)strlen(s);
249 if(leng >= (RuleSpaceSize - cd->PrefixSpace))
250 leng = RuleSpaceSize - cd->PrefixSpace - 1;
251 g_snprintf(RuleSpace, (size_t)RuleSpaceSize, "%*s%*.*s ", cd->PrefixSpace, "",
253 txtlength = cd->PrefixSpace + leng + 1;
254 nbrules = ColWidth(From,To) - txtlength;
255 for(tmp=RuleSpace + txtlength, i=nbrules ; i>0; tmp++,i--)
267 ColumnInfo *cd= &ColumnData[cn];
268 static char PrtBuf[256];
270 g_snprintf(PrtBuf, SIZEOF(PrtBuf),
271 "%*s", cd->Width, "-- ");
273 g_snprintf(PrtBuf, SIZEOF(PrtBuf),
274 cd->Format, cd->Width, cd->Precision, a/b);
283 if ((ch = getc(logfile)) != EOF) {
284 if (ungetc(ch, logfile) == EOF) {
285 if (ferror(logfile)) {
286 error(_("ungetc failed: %s\n"), strerror(errno));
289 error(_("ungetc failed: EOF\n"));
303 /* allocate new line node */
304 new = (line_t *) alloc(SIZEOF(line_t));
307 new->str = stralloc(str);
309 /* add to end of list */
326 error(_("Usage: amreport conf [-i] [-M address] [-f output-file] [-l logfile] [-p postscript-file] [-o configoption]*"));
338 char *logfname, *psfname, *outfname, *subj_str = NULL;
341 char *mail_cmd = NULL, *printer_cmd = NULL;
344 char *ColumnSpec = "";
349 char *lbl_templ = NULL;
350 config_overwrites_t *cfg_ovr = NULL;
351 char *cfg_opt = NULL;
355 * Configure program for internationalization:
356 * 1) Only set the message locale for now.
357 * 2) Set textdomain for all amanda related programs to "amanda"
358 * We don't want to be forced to support dozens of message catalogs.
360 setlocale(LC_MESSAGES, "C");
361 textdomain("amanda");
365 set_pname("amreport");
367 dbopen(DBG_SUBDIR_SERVER);
369 /* Don't die when child closes pipe */
370 signal(SIGPIPE, SIG_IGN);
372 /* Process options */
374 erroutput_type = ERR_INTERACTIVE;
380 cwd = g_get_current_dir();
382 error(_("Cannot determine current working directory: %s"),
388 if (argv[1][0] == '-') {
393 /* get the config name and move past it */
397 cfg_ovr = new_config_overwrites(argc/2);
398 while((opt = getopt(argc, argv, "o:M:f:l:p:i")) != EOF) {
404 if (mailto != NULL) {
405 error(_("you may specify at most one -M"));
408 mailto = stralloc(optarg);
409 if(!validate_mailto(mailto)) {
410 error(_("mail address has invalid characters"));
415 if (outfname != NULL) {
416 error(_("you may specify at most one -f"));
419 if (*optarg == '/') {
420 outfname = stralloc(optarg);
422 outfname = vstralloc(cwd, "/", optarg, NULL);
427 if (logfname != NULL) {
428 error(_("you may specify at most one -l"));
431 if (*optarg == '/') {
432 logfname = stralloc(optarg);
434 logfname = vstralloc(cwd, "/", optarg, NULL);
438 if (psfname != NULL) {
439 error(_("you may specify at most one -p"));
442 if (*optarg == '/') {
443 psfname = stralloc(optarg);
445 psfname = vstralloc(cwd, "/", optarg, NULL);
449 add_config_overwrite_opt(cfg_ovr, optarg);
462 if( !mailout && mailto ){
463 g_printf(_("You cannot specify both -i & -M at the same time\n"));
469 /* read configuration files */
471 /* ignore any errors reading the config file (amreport can run without a config) */
472 config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
473 apply_config_overwrites(cfg_ovr);
475 if (config_errors(NULL) >= CFGERR_WARNINGS) {
476 config_print_errors();
479 check_running_as(RUNNING_AS_DUMPUSER);
481 dbrename(get_config_name(), DBG_SUBDIR_SERVER);
483 safe_cd(); /* must be called *after* config_init() */
485 mailer = getconf_str(CNF_MAILER);
486 if (mailer && *mailer == '\0')
488 if (!mailer && !outfname) {
489 g_printf(_("You must run amreport with '-f <output file>' because a mailer is not defined\n"));
493 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
494 /* Ignore error from read_diskfile */
495 read_diskfile(conf_diskfile, &diskq);
496 amfree(conf_diskfile);
497 if(mailout && !mailto &&
498 getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0) {
499 mailto = getconf_str(CNF_MAILTO);
500 if(!validate_mailto(mailto)){
505 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
506 /* Ignore error from read_tapelist */
507 read_tapelist(conf_tapelist);
508 amfree(conf_tapelist);
509 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
510 if(open_infofile(conf_infofile)) {
511 error(_("could not open info db \"%s\""), conf_infofile);
514 amfree(conf_infofile);
516 displayunit = getconf_str(CNF_DISPLAYUNIT);
517 unitdivisor = getconf_unit_divisor();
519 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
520 if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
522 curprog = P_REPORTER;
527 ColumnSpec = ""; /* use the default */
528 if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
530 curprog = P_REPORTER;
537 for (cn = 0; ColumnData[cn].Name != NULL; cn++) {
538 if (ColumnData[cn].MaxWidth) {
539 MaxWidthsRequested = 1;
547 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
548 logfname = vstralloc(conf_logdir, "/", "log", NULL);
552 if((logfile = fopen(logfname, "r")) == NULL) {
554 curprog = P_REPORTER;
555 curstr = vstralloc(_("could not open log "),
564 while(logfile && get_logline(logfile)) {
566 case L_START: handle_start(); break;
567 case L_FINISH: handle_finish(); break;
569 case L_INFO: handle_note(); break;
570 case L_WARNING: handle_note(); break;
572 case L_SUMMARY: handle_summary(); break;
573 case L_STATS: handle_stats(); break;
575 case L_ERROR: handle_error(); break;
576 case L_FATAL: handle_error(); break;
578 case L_DISK: handle_disk(); break;
580 case L_DONE: handle_success(curlog); break;
581 case L_SUCCESS: handle_success(curlog); break;
582 case L_CHUNKSUCCESS: handle_success(curlog); break;
583 case L_PART: handle_chunk(curlog); break;
584 case L_PARTPARTIAL: handle_chunk(curlog); break;
585 case L_CHUNK: handle_chunk(curlog); break;
586 case L_PARTIAL: handle_partial(); break;
587 case L_STRANGE: handle_strange(); break;
588 case L_FAIL: handle_failed(); break;
592 curprog = P_REPORTER;
593 curstr = vstrallocf(_("unexpected log line: %s"), curstr);
602 generate_bad_estimate();
605 subj_str = vstralloc(getconf_str(CNF_ORG),
606 " ", amflush_run ? "AMFLUSH" : "AMANDA",
607 " ", "MAIL REPORT FOR",
608 " ", nicedate(run_datestamp ? run_datestamp : "0"),
611 /* lookup the tapetype and printer type from the amanda.conf file. */
612 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
613 printer = getconf_str(CNF_PRINTER);
615 /* ignore SIGPIPE so if a child process dies we do not also go away */
616 signal(SIGPIPE, SIG_IGN);
618 /* open pipe to print spooler if necessary) */
621 /* if the postscript_label_template (tp->lbl_templ) field is not */
622 /* the empty string (i.e. it is set to something), open the */
623 /* postscript debugging file for writing. */
625 lbl_templ = tapetype_get_lbl_templ(tp);
626 if (tp && lbl_templ && strcmp(lbl_templ, "") != 0) {
627 if ((postscript = fopen(psfname, "w")) == NULL) {
629 curprog = P_REPORTER;
630 curstr = vstrallocf(_("could not open %s: %s"),
639 if (strcmp(printer, "") != 0) /* alternate printer is defined */
640 /* print to the specified printer */
642 printer_cmd = vstralloc(LPRCMD, " ", LPRFLAG, printer, NULL);
644 printer_cmd = vstralloc(LPRCMD, NULL);
647 /* print to the default printer */
648 printer_cmd = vstralloc(LPRCMD, NULL);
651 lbl_templ = tapetype_get_lbl_templ(tp);
652 if (tp && lbl_templ && strcmp(lbl_templ, "") != 0) {
654 if ((postscript = popen(printer_cmd, "w")) == NULL) {
656 curprog = P_REPORTER;
657 curstr = vstrallocf(_("could not open pipe to %s: %s"),
658 printer_cmd, strerror(errno));
664 curprog = P_REPORTER;
665 curstr = vstrallocf(_("no printer command defined"));
674 /* open pipe to mailer */
677 /* output to a file */
678 if((mailf = fopen(outfname,"w")) == NULL) {
679 error(_("could not open output file: %s %s"), outfname, strerror(errno));
682 if (mailto != NULL) {
683 g_fprintf(mailf, "To: %s\n", mailto);
684 g_fprintf(mailf, "Subject: %s\n\n", subj_str);
689 send_amreport_t send_amreport;
692 send_amreport = getconf_send_amreport(CNF_SEND_AMREPORT_ON);
693 do_mail = send_amreport == SEND_AMREPORT_ALL ||
694 (send_amreport == SEND_AMREPORT_STRANGE &&
695 (!got_finish || first_failed || errsum ||
696 first_strange || errdet || strangedet)) ||
697 (send_amreport == SEND_AMREPORT_ERROR &&
698 (!got_finish || first_failed || errsum || errdet));
700 mail_cmd = vstralloc(mailer,
701 " -s", " \"", subj_str, "\"",
703 if((mailf = popen(mail_cmd, "w")) == NULL) {
704 error(_("could not open pipe to \"%s\": %s"),
705 mail_cmd, strerror(errno));
712 g_printf(_("No mail sent! "));
713 g_printf(_("No valid mail address has been specified in amanda.conf or on the commmand line\n"));
723 if(!got_finish) fputs(_("*** THE DUMPS DID NOT FINISH PROPERLY!\n\n"), mailf);
726 g_fprintf(mailf, _("Hostname: %s\n"), ghostname);
727 g_fprintf(mailf, _("Org : %s\n"), getconf_str(CNF_ORG));
728 g_fprintf(mailf, _("Config : %s\n"), get_config_name());
729 g_fprintf(mailf, _("Date : %s\n"),
730 nicedate(run_datestamp ? run_datestamp : "0"));
731 g_fprintf(mailf,"\n");
736 if(first_failed || errsum) {
737 g_fprintf(mailf,_("\nFAILURE DUMP SUMMARY:\n"));
738 if(first_failed) output_X_summary(first_failed);
739 if(errsum) output_lines(errsum, mailf);
742 g_fprintf(mailf,_("\nSTRANGE DUMP SUMMARY:\n"));
743 if(first_strange) output_X_summary(first_strange);
745 fputs("\n\n", mailf);
750 g_fprintf(mailf,"\n\f\n");
751 g_fprintf(mailf,_("FAILED DUMP DETAILS:\n"));
752 output_lines(errdet, mailf);
755 g_fprintf(mailf,"\n\f\n");
756 g_fprintf(mailf,_("STRANGE DUMP DETAILS:\n"));
757 output_lines(strangedet, mailf);
760 g_fprintf(mailf,"\n\f\n");
761 g_fprintf(mailf,_("NOTES:\n"));
762 output_lines(notes, mailf);
764 if(sortq.head != NULL) {
765 g_fprintf(mailf,"\n\f\n");
766 g_fprintf(mailf,_("DUMP SUMMARY:\n"));
769 g_fprintf(mailf,_("\n(brought to you by Amanda version %s)\n"),
774 do_postscript_output();
778 /* close postscript file */
779 if (psfname && postscript) {
780 /* it may be that postscript is NOT opened */
784 if (postscript != NULL && pclose(postscript) != 0) {
785 error(_("printer command failed: %s"), printer_cmd);
791 /* close output file */
797 if((exitcode = pclose(mailf)) != 0) {
798 char *exitstr = str_exit_status("mail command", exitcode);
799 error("%s", exitstr);
806 free_disklist(&diskq);
807 amfree(run_datestamp);
819 #define mb(f) ((f)/1024) /* kbytes -> mbutes */
820 #define du(f) ((f)/unitdivisor) /* kbytes -> displayunit */
821 #define pct(f) ((f)*100.0) /* percent */
822 #define hrmn(f) ((int)(f)+30)/3600, (((int)(f)+30)%3600)/60
823 #define mnsc(f) ((int)(f+0.5))/60, ((int)(f+0.5)) % 60
825 #define divzero(fp,a,b) \
829 g_fprintf((fp)," -- "); \
830 else if ((q = (a)/q) >= 99999.95) \
831 g_fprintf((fp), "#####"); \
832 else if (q >= 999.95) \
833 g_fprintf((fp), "%5.0lf",q); \
835 g_fprintf((fp), "%5.1lf",q); \
837 #define divzero_wide(fp,a,b) \
841 g_fprintf((fp)," -- "); \
842 else if ((q = (a)/q) >= 9999999.95) \
843 g_fprintf((fp), "#######"); \
844 else if (q >= 99999.95) \
845 g_fprintf((fp), "%7.0lf",q); \
847 g_fprintf((fp), "%7.1lf",q); \
854 tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
860 tapesize = tapetype_get_length(tp);
861 marksize = tapetype_get_filemark(tp);
863 tapesize = 100 * 1024 * 1024;
864 marksize = 1 * 1024 * 1024;
867 stats[2].dumpdisks = stats[0].dumpdisks + stats[1].dumpdisks;
868 stats[2].tapedisks = stats[0].tapedisks + stats[1].tapedisks;
869 stats[2].tapechunks = stats[0].tapechunks + stats[1].tapechunks;
870 stats[2].outsize = stats[0].outsize + stats[1].outsize;
871 stats[2].origsize = stats[0].origsize + stats[1].origsize;
872 stats[2].tapesize = stats[0].tapesize + stats[1].tapesize;
873 stats[2].coutsize = stats[0].coutsize + stats[1].coutsize;
874 stats[2].corigsize = stats[0].corigsize + stats[1].corigsize;
875 stats[2].taper_time = stats[0].taper_time + stats[1].taper_time;
876 stats[2].dumper_time = stats[0].dumper_time + stats[1].dumper_time;
878 if(!got_finish) /* no driver finish line, estimate total run time */
879 total_time = stats[2].taper_time + planner_time;
881 idle_time = (total_time - startup_time) - stats[2].taper_time;
882 if(idle_time < 0) idle_time = 0.0;
884 g_fprintf(mailf,_("STATISTICS:\n"));
886 _(" Total Full Incr.\n"));
888 _(" -------- -------- --------\n"));
891 _("Estimate Time (hrs:min) %2d:%02d\n"), hrmn(planner_time));
894 _("Run Time (hrs:min) %2d:%02d\n"), hrmn(total_time));
897 _("Dump Time (hrs:min) %2d:%02d %2d:%02d %2d:%02d\n"),
898 hrmn(stats[2].dumper_time), hrmn(stats[0].dumper_time),
899 hrmn(stats[1].dumper_time));
902 _("Output Size (meg) %8.1lf %8.1lf %8.1lf\n"),
903 mb(stats[2].outsize), mb(stats[0].outsize), mb(stats[1].outsize));
906 _("Original Size (meg) %8.1lf %8.1lf %8.1lf\n"),
907 mb(stats[2].origsize), mb(stats[0].origsize),
908 mb(stats[1].origsize));
910 g_fprintf(mailf, _("Avg Compressed Size (%%) "));
911 divzero(mailf, pct(stats[2].coutsize),stats[2].corigsize);
912 fputs(_(" "), mailf);
913 divzero(mailf, pct(stats[0].coutsize),stats[0].corigsize);
914 fputs(_(" "), mailf);
915 divzero(mailf, pct(stats[1].coutsize),stats[1].corigsize);
917 if(stats[1].dumpdisks > 0) fputs(_(" (level:#disks ...)"), mailf);
921 _("Filesystems Dumped %4d %4d %4d"),
922 stats[2].dumpdisks, stats[0].dumpdisks, stats[1].dumpdisks);
924 if(stats[1].dumpdisks > 0) {
926 for(lv = 1; lv < 10; lv++) if(dumpdisks[lv]) {
927 fputs(first?_(" ("):_(" "), mailf);
929 g_fprintf(mailf, _("%d:%d"), lv, dumpdisks[lv]);
935 g_fprintf(mailf, _("Avg Dump Rate (k/s) "));
936 divzero_wide(mailf, stats[2].outsize,stats[2].dumper_time);
937 fputs(_(" "), mailf);
938 divzero_wide(mailf, stats[0].outsize,stats[0].dumper_time);
939 fputs(_(" "), mailf);
940 divzero_wide(mailf, stats[1].outsize,stats[1].dumper_time);
945 _("Tape Time (hrs:min) %2d:%02d %2d:%02d %2d:%02d\n"),
946 hrmn(stats[2].taper_time), hrmn(stats[0].taper_time),
947 hrmn(stats[1].taper_time));
950 _("Tape Size (meg) %8.1lf %8.1lf %8.1lf\n"),
951 mb(stats[2].tapesize), mb(stats[0].tapesize),
952 mb(stats[1].tapesize));
954 g_fprintf(mailf, _("Tape Used (%%) "));
955 divzero(mailf, pct(stats[2].tapesize+marksize*(stats[2].tapedisks+stats[2].tapechunks)),(double)tapesize);
956 fputs(_(" "), mailf);
957 divzero(mailf, pct(stats[0].tapesize+marksize*(stats[0].tapedisks+stats[0].tapechunks)),(double)tapesize);
958 fputs(_(" "), mailf);
959 divzero(mailf, pct(stats[1].tapesize+marksize*(stats[1].tapedisks+stats[1].tapechunks)),(double)tapesize);
961 if(stats[1].tapedisks > 0) fputs(_(" (level:#disks ...)"), mailf);
965 _("Filesystems Taped %4d %4d %4d"),
966 stats[2].tapedisks, stats[0].tapedisks, stats[1].tapedisks);
968 if(stats[1].tapedisks > 0) {
970 for(lv = 1; lv < 10; lv++) if(tapedisks[lv]) {
971 fputs(first?_(" ("):_(" "), mailf);
973 g_fprintf(mailf, _("%d:%d"), lv, tapedisks[lv]);
979 if(stats[1].tapechunks > 0) fputs(_(" (level:#chunks ...)"), mailf);
983 _("Chunks Taped %4d %4d %4d"),
984 stats[2].tapechunks, stats[0].tapechunks, stats[1].tapechunks);
986 if(stats[1].tapechunks > 0) {
988 for(lv = 1; lv < 10; lv++) if(tapechunks[lv]) {
989 fputs(first?_(" ("):_(" "), mailf);
991 g_fprintf(mailf, _("%d:%d"), lv, tapechunks[lv]);
997 g_fprintf(mailf, _("Avg Tp Write Rate (k/s) "));
998 divzero_wide(mailf, stats[2].tapesize,stats[2].taper_time);
999 fputs(_(" "), mailf);
1000 divzero_wide(mailf, stats[0].tapesize,stats[0].taper_time);
1001 fputs(_(" "), mailf);
1002 divzero_wide(mailf, stats[1].tapesize,stats[1].taper_time);
1006 int label_length = (int)strlen(stats_by_tape->label) + 5;
1007 g_fprintf(mailf,_("\nUSAGE BY TAPE:\n"));
1008 g_fprintf(mailf,_(" %-*s Time Size %% Nb Nc\n"),
1009 label_length, _("Label"));
1010 for(current_tape = stats_by_tape; current_tape != NULL;
1011 current_tape = current_tape->next) {
1012 g_fprintf(mailf, _(" %-*s"), label_length, current_tape->label);
1013 g_fprintf(mailf, _(" %2d:%02d"), hrmn(current_tape->taper_time));
1014 g_fprintf(mailf, _(" %8.0lf%s "), du(current_tape->coutsize), displayunit);
1015 divzero(mailf, pct(current_tape->coutsize + marksize *
1016 (current_tape->tapedisks+current_tape->tapechunks)),
1018 g_fprintf(mailf, _(" %4d"), current_tape->tapedisks);
1019 g_fprintf(mailf, _(" %4d\n"), current_tape->tapechunks);
1027 output_tapeinfo(void)
1034 if (last_run_tapes > 0) {
1037 plural(_("The dumps were flushed to tape %s.\n"),
1038 _("The dumps were flushed to tapes %s.\n"),
1040 tape_labels ? tape_labels : "");
1043 plural(_("These dumps were to tape %s.\n"),
1044 _("These dumps were to tapes %s.\n"),
1046 tape_labels ? tape_labels : "");
1051 _("*** A TAPE ERROR OCCURRED: %s.\n"), tapestart_error);
1053 if (cmdlogfname == 1) {
1055 fputs(_("Some dumps may have been left in the holding disk.\n"),
1057 g_fprintf(mailf,"\n");
1060 GSList *holding_list, *holding_file;
1061 off_t h_size = 0, mh_size;
1063 holding_list = holding_get_files_for_flush(NULL);
1064 for(holding_file=holding_list; holding_file != NULL;
1065 holding_file = holding_file->next) {
1066 mh_size = holding_file_size((char *)holding_file->data, 1);
1073 _("There are %lld%s of dumps left in the holding disk.\n"),
1074 (long long)du(h_size), displayunit);
1075 if (getconf_boolean(CNF_AUTOFLUSH)) {
1076 g_fprintf(mailf, _("They will be flushed on the next run.\n"));
1078 g_fprintf(mailf, _("Run amflush to flush them to tape.\n"));
1080 g_fprintf(mailf,"\n");
1081 } else if (degraded_mode) {
1082 g_fprintf(mailf, _("No dumps are left in the holding disk. %lld%s\n"), (long long)h_size, displayunit);
1083 g_fprintf(mailf,"\n");
1087 tp = lookup_last_reusable_tape(skip);
1089 run_tapes = getconf_int(CNF_RUNTAPES);
1092 fputs(_("The next tape Amanda expects to use is: "), mailf);
1093 else if(run_tapes > 1)
1094 g_fprintf(mailf, _("The next %d tapes Amanda expects to use are: "),
1098 for (i=0 ; i < run_tapes ; i++) {
1100 if (nb_new_tape > 0) {
1101 if (nb_new_tape == 1)
1102 g_fprintf(mailf, _("1 new tape, "));
1104 g_fprintf(mailf, _("%d new tapes, "), nb_new_tape);
1107 g_fprintf(mailf, "%s", tp->label);
1108 if (i < run_tapes-1) fputs(", ", mailf);
1114 tp = lookup_last_reusable_tape(skip);
1116 if (nb_new_tape > 0) {
1117 if (nb_new_tape == 1)
1118 g_fprintf(mailf, _("1 new tape"));
1120 g_fprintf(mailf, _("%d new tapes"), nb_new_tape);
1122 fputs(".\n", mailf);
1124 run_tapes = getconf_int(CNF_RUNTAPES);
1125 print_new_tapes(mailf, run_tapes);
1133 size_t len_host=0, len_disk=0;
1134 X_summary_t *strange;
1137 for(strange=first; strange != NULL; strange = strange->next) {
1138 if(strlen(strange->hostname) > len_host)
1139 len_host = strlen(strange->hostname);
1140 if(strlen(strange->diskname) > len_disk)
1141 len_disk = strlen(strange->diskname);
1143 for(strange=first; strange != NULL; strange = strange->next) {
1144 str = vstralloc(" ", prefixstrange(strange->hostname, strange->diskname, strange->level, len_host, len_disk),
1145 " ", strange->str, NULL);
1146 g_fprintf(mailf, "%s\n", str);
1177 rc = strcmp(a->host->hostname, b->host->hostname);
1178 if(rc == 0) rc = strcmp(a->name, b->name);
1187 sortq.head = sortq.tail = NULL;
1188 while(!empty(diskq)) {
1189 dp = dequeue_disk(&diskq);
1190 if(data(dp) == NULL) { /* create one */
1191 find_repdata(dp, run_datestamp, 0);
1193 insert_disk(&sortq, dp, sort_by_name);
1203 int l = (int)strlen(s);
1219 g_snprintf(testBuf, SIZEOF(testBuf),
1220 cd->Format, cd->Width, cd->Precision, n);
1221 l = (int)strlen(testBuf);
1236 g_snprintf(testBuf, SIZEOF(testBuf),
1237 cd->Format, cd->Width, cd->Precision, d);
1238 l = (int)strlen(testBuf);
1244 static int HostName;
1249 static int Compress;
1250 static int DumpTime;
1251 static int DumpRate;
1252 static int TapeTime;
1253 static int TapeRate;
1258 /* we have to look for columspec's, that require the recalculation.
1259 * we do here the same loops over the sortq as is done in
1260 * output_summary. So, if anything is changed there, we have to
1261 * change this here also.
1270 for (i=0;ColumnData[i].Name != NULL; i++) {
1271 if (ColumnData[i].MaxWidth) {
1272 l = (int)strlen(ColumnData[i].Title);
1273 if (ColumnData[i].Width < l)
1274 ColumnData[i].Width= l;
1278 for(dp = sortq.head; dp != NULL; dp = dp->next) {
1280 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1281 char TimeRateBuffer[40];
1283 CheckStringMax(&ColumnData[HostName], dp->host->hostname);
1284 qdevname = quote_string(dp->name);
1285 CheckStringMax(&ColumnData[Disk], qdevname);
1287 if (repdata->dumper.result == L_BOGUS &&
1288 repdata->taper.result == L_BOGUS)
1290 CheckIntMax(&ColumnData[Level], repdata->level);
1291 if(repdata->dumper.result == L_SUCCESS ||
1292 repdata->dumper.result == L_CHUNKSUCCESS) {
1293 CheckFloatMax(&ColumnData[OrigKB],
1294 (double)du(repdata->dumper.origsize));
1295 CheckFloatMax(&ColumnData[OutKB],
1296 (double)du(repdata->dumper.outsize));
1297 if(abs(repdata->dumper.outsize - repdata->dumper.origsize)< 32)
1300 f = repdata->dumper.origsize;
1301 CheckStringMax(&ColumnData[Compress],
1302 sDivZero(pct(repdata->dumper.outsize), f, Compress));
1305 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1306 "%3d:%02d", mnsc(repdata->dumper.sec));
1308 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1310 CheckStringMax(&ColumnData[DumpTime], TimeRateBuffer);
1312 CheckFloatMax(&ColumnData[DumpRate], repdata->dumper.kps);
1315 if(repdata->taper.result == L_FAIL) {
1316 CheckStringMax(&ColumnData[TapeTime], "FAILED");
1319 if(repdata->taper.result == L_SUCCESS ||
1320 repdata->taper.result == L_CHUNKSUCCESS)
1321 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1322 "%3d:%02d", mnsc(repdata->taper.sec));
1324 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1326 CheckStringMax(&ColumnData[TapeTime], TimeRateBuffer);
1328 if(repdata->taper.result == L_SUCCESS ||
1329 repdata->taper.result == L_CHUNKSUCCESS)
1330 CheckFloatMax(&ColumnData[TapeRate], repdata->taper.kps);
1332 CheckStringMax(&ColumnData[TapeRate], " ");
1339 output_summary(void)
1343 char *ds="DUMPER STATS";
1344 char *ts=" TAPER STATS";
1347 int i, h, w1, wDump, wTape;
1348 double outsize, origsize;
1352 HostName = StringToColumn("HostName");
1353 Disk = StringToColumn("Disk");
1354 Level = StringToColumn("Level");
1355 OrigKB = StringToColumn("OrigKB");
1356 OutKB = StringToColumn("OutKB");
1357 Compress = StringToColumn("Compress");
1358 DumpTime = StringToColumn("DumpTime");
1359 DumpRate = StringToColumn("DumpRate");
1360 TapeTime = StringToColumn("TapeTime");
1361 TapeRate = StringToColumn("TapeRate");
1363 /* at first determine if we have to recalculate our widths */
1364 if (MaxWidthsRequested)
1367 /* title for Dumper-Stats */
1368 w1= ColWidth(HostName, Level);
1369 wDump= ColWidth(OrigKB, DumpRate);
1370 wTape= ColWidth(TapeTime, TapeRate);
1372 /* print centered top titles */
1373 h = (int)strlen(ds);
1379 g_fprintf(mailf, "%*s", w1+h, "");
1380 g_fprintf(mailf, "%-*s", wDump-h, ds);
1381 h = (int)strlen(ts);
1387 g_fprintf(mailf, "%*s", h, "");
1388 g_fprintf(mailf, "%-*s", wTape-h, ts);
1391 /* print the titles */
1392 for (i=0; ColumnData[i].Name != NULL; i++) {
1394 ColumnInfo *cd= &ColumnData[i];
1395 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1396 if (cd->Format[1] == '-')
1400 if(strcmp(cd->Title,"ORIG-KB") == 0) {
1401 /* cd->Title must be re-allocated in write-memory */
1402 cd->Title = stralloc("ORIG-KB");
1403 cd->Title[5] = displayunit[0];
1405 if(strcmp(cd->Title,"OUT-KB") == 0) {
1406 /* cd->Title must be re-allocated in write-memory */
1407 cd->Title = stralloc("OUT-KB");
1408 cd->Title[4] = displayunit[0];
1410 g_fprintf(mailf, fmt, cd->Width, cd->Title);
1414 /* print the rules */
1415 fputs(tmp=Rule(HostName, Level), mailf); amfree(tmp);
1416 fputs(tmp=Rule(OrigKB, DumpRate), mailf); amfree(tmp);
1417 fputs(tmp=Rule(TapeTime, TapeRate), mailf); amfree(tmp);
1420 for(dp = sortq.head; dp != NULL; dp = dp->next) {
1423 char TimeRateBuffer[40];
1424 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1429 cd= &ColumnData[HostName];
1430 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1431 g_fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->host->hostname);
1433 cd= &ColumnData[Disk];
1434 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1435 devname = sanitize_string(dp->name);
1436 qdevname = quote_string(devname);
1437 devlen = strlen(qdevname);
1438 if (devlen > (size_t)cd->Width) {
1440 if (strcmp(devname, qdevname)) {
1445 g_fprintf(mailf, cd->Format, cd->Width-nb, cd->Precision-nb,
1446 qdevname+devlen - (cd->Width-nb) );
1449 g_fprintf(mailf, cd->Format, cd->Width, cd->Width, qdevname);
1452 cd= &ColumnData[Level];
1453 if (repdata->dumper.result == L_BOGUS &&
1454 repdata->taper.result == L_BOGUS) {
1456 g_fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1457 tmp=TextRule(OrigKB, TapeRate, "NO FILE TO FLUSH"));
1459 g_fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1460 tmp=TextRule(OrigKB, TapeRate, "MISSING"));
1466 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1467 g_fprintf(mailf, cd->Format, cd->Width, cd->Precision,repdata->level);
1469 if (repdata->dumper.result == L_SKIPPED) {
1470 g_fprintf(mailf, "%s\n",
1471 tmp=TextRule(OrigKB, TapeRate, "SKIPPED"));
1475 if (repdata->dumper.result == L_FAIL && (repdata->chunker.result != L_PARTIAL && repdata->taper.result != L_PARTIAL)) {
1476 g_fprintf(mailf, "%s\n",
1477 tmp=TextRule(OrigKB, TapeRate, "FAILED"));
1479 exit_status |= STATUS_FAILED;
1483 if(repdata->dumper.result == L_SUCCESS ||
1484 repdata->dumper.result == L_CHUNKSUCCESS)
1485 origsize = repdata->dumper.origsize;
1486 else if(repdata->taper.result == L_SUCCESS ||
1487 repdata->taper.result == L_PARTIAL)
1488 origsize = repdata->taper.origsize;
1490 origsize = repdata->chunker.origsize;
1492 if(repdata->taper.result == L_SUCCESS ||
1493 repdata->taper.result == L_CHUNKSUCCESS)
1494 outsize = repdata->taper.outsize;
1495 else if(repdata->chunker.result == L_SUCCESS ||
1496 repdata->chunker.result == L_PARTIAL ||
1497 repdata->chunker.result == L_CHUNKSUCCESS)
1498 outsize = repdata->chunker.outsize;
1499 else if (repdata->taper.result == L_PARTIAL)
1500 outsize = repdata->taper.outsize;
1502 outsize = repdata->dumper.outsize;
1504 cd= &ColumnData[OrigKB];
1505 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1506 if(isnormal(origsize))
1507 g_fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(origsize));
1509 g_fprintf(mailf, "%*.*s", cd->Width, cd->Width, "");
1511 cd= &ColumnData[OutKB];
1512 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1514 g_fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(outsize));
1516 cd= &ColumnData[Compress];
1517 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1519 if(abs(outsize - origsize) < 32)
1521 else if(origsize < 1.0)
1526 fputs(sDivZero(pct(outsize), f, Compress), mailf);
1528 cd= &ColumnData[DumpTime];
1530 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1531 if(repdata->dumper.result == L_SUCCESS ||
1532 repdata->dumper.result == L_CHUNKSUCCESS) {
1533 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1534 "%3d:%02d", mnsc(repdata->dumper.sec));
1535 g_fprintf(mailf, cd->Format, cd->Width, cd->Width,
1538 cdWidth = cd->Width;
1541 cd= &ColumnData[DumpRate];
1542 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1543 if (repdata->dumper.result == L_SUCCESS ||
1544 repdata->dumper.result == L_CHUNKSUCCESS) {
1545 g_fprintf(mailf, cd->Format, cd->Width, cd->Precision,
1546 repdata->dumper.kps);
1547 } else if (repdata->dumper.result == L_FAIL) {
1548 if (repdata->chunker.result == L_PARTIAL ||
1549 repdata->taper.result == L_PARTIAL) {
1551 cdWidth += cd->Width;
1552 i = (cdWidth - strlen("PARTIAL")) / 2;
1553 g_fprintf(mailf, "%*s%*s", cdWidth-i, "PARTIAL", i, "");
1556 cdWidth += cd->Width;
1557 i = (cdWidth - strlen("FAILED")) / 2;
1558 g_fprintf(mailf, "%*s%*s", cdWidth-i, "FAILED", i, "");
1560 } else if (repdata->dumper.result == L_BOGUS) {
1562 cdWidth += cd->Width;
1563 i = (cdWidth - strlen("FLUSH")) / 2;
1564 g_fprintf(mailf, "%*s%*s", cdWidth-i, "FLUSH", i, "");
1566 cdWidth += cd->Width;
1567 g_fprintf(mailf, "%*s", cdWidth, "");
1570 cd= &ColumnData[TapeTime];
1571 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1572 if(repdata->taper.result == L_FAIL) {
1573 g_fprintf(mailf, "%s\n",
1574 tmp=TextRule(TapeTime, TapeRate, "FAILED "));
1579 if(repdata->taper.result == L_SUCCESS ||
1580 repdata->taper.result == L_PARTIAL ||
1581 repdata->taper.result == L_CHUNKSUCCESS)
1582 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1583 "%3d:%02d", mnsc(repdata->taper.sec));
1585 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1587 g_fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
1589 cd= &ColumnData[TapeRate];
1590 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1591 if(repdata->taper.result == L_SUCCESS ||
1592 repdata->taper.result == L_PARTIAL ||
1593 repdata->taper.result == L_CHUNKSUCCESS)
1594 g_fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->taper.kps);
1596 g_fprintf(mailf, "%*s", cd->Width, " ");
1598 if (repdata->chunker.result == L_PARTIAL)
1599 g_fprintf(mailf, " PARTIAL");
1600 else if(repdata->taper.result == L_PARTIAL)
1601 g_fprintf(mailf, " TAPE-PARTIAL");
1611 const char *err_text)
1614 s = g_strdup_printf(_("line %d of log is bogus: <%s %s %s>\n"),
1616 logtype_str[curlog], program_str[curprog], curstr);
1617 g_printf("%s\n", s);
1618 g_printf(_(" Scan failed at: <%s>\n"), err_text);
1619 addline(&errsum, s);
1625 * Formats an integer of the form YYYYMMDDHHMMSS into the string
1626 * "Monthname DD, YYYY". A pointer to the statically allocated string
1627 * is returned, so it must be copied to other storage (or just printed)
1628 * before calling nicedate() again.
1632 const char *datestamp)
1634 static char nice[64];
1637 static char *months[13] = {
1652 int year, month, day;
1654 strncpy(date, datestamp, 8);
1656 numdate = atoi(date);
1657 year = numdate / 10000;
1658 day = numdate % 100;
1659 month = (numdate / 100) % 100;
1663 g_snprintf(nice, SIZEOF(nice), "%s %d, %d", _(months[month]), day, year);
1671 static int started = 0;
1681 skip_whitespace(s, ch);
1682 if(ch == '\0' || strncmp_const_skip(s - 1, "datestamp", s, ch) != 0) {
1687 skip_whitespace(s, ch);
1693 skip_non_whitespace(s, ch);
1695 run_datestamp = newstralloc(run_datestamp, fp);
1698 skip_whitespace(s, ch);
1699 if(ch == '\0' || strncmp_const_skip(s - 1, "label", s, ch) != 0) {
1704 skip_whitespace(s, ch);
1710 skip_non_whitespace(s, ch);
1713 label = stralloc(fp);
1717 fp = vstralloc(tape_labels, ", ", label, NULL);
1718 amfree(tape_labels);
1721 tape_labels = stralloc(label);
1726 if(stats_by_tape == NULL) {
1727 stats_by_tape = current_tape = (taper_t *)alloc(SIZEOF(taper_t));
1730 current_tape->next = (taper_t *)alloc(SIZEOF(taper_t));
1731 current_tape = current_tape->next;
1733 current_tape->label = label;
1734 current_tape->taper_time = 0.0;
1735 current_tape->coutsize = 0.0;
1736 current_tape->corigsize = 0.0;
1737 current_tape->tapedisks = 0;
1738 current_tape->tapechunks = 0;
1739 current_tape->next = NULL;
1759 skip_whitespace(s, ch);
1760 if(ch == '\0' || strncmp_const_skip(s - 1, "date", s, ch) != 0) {
1761 return; /* ignore bogus line */
1764 skip_whitespace(s, ch);
1770 skip_non_whitespace(s, ch);
1772 run_datestamp = newstralloc(run_datestamp, fp);
1777 if(amflush_run && normal_run) {
1780 _(" reporter: both amflush and planner output in log, ignoring amflush."));
1792 if(curprog == P_DRIVER || curprog == P_AMFLUSH || curprog == P_PLANNER) {
1796 skip_whitespace(s, ch);
1797 if(ch == '\0' || strncmp_const_skip(s - 1, "date", s, ch) != 0) {
1802 skip_whitespace(s, ch);
1807 skip_non_whitespace(s, ch); /* ignore the date string */
1809 skip_whitespace(s, ch);
1810 if(ch == '\0' || strncmp_const_skip(s - 1, "time", s, ch) != 0) {
1811 /* older planner doesn't write time */
1812 if(curprog == P_PLANNER) return;
1817 skip_whitespace(s, ch);
1822 if(sscanf(s - 1, "%lf", &a_time) != 1) {
1826 if(curprog == P_PLANNER) {
1827 planner_time = a_time;
1830 total_time = a_time;
1841 char *hostname, *diskname, *datestamp, *qdiskname;
1843 double sec, kps, nbytes, cbytes;
1847 if(curprog == P_DRIVER) {
1851 skip_whitespace(s, ch);
1852 if(ch != '\0' && strncmp_const_skip(s - 1, "startup time", s, ch) == 0) {
1853 skip_whitespace(s, ch);
1858 if(sscanf(s - 1, "%lf", &startup_time) != 1) {
1862 planner_time = startup_time;
1864 else if(ch != '\0' && strncmp_const_skip(s - 1, "hostname", s, ch) == 0) {
1865 skip_whitespace(s, ch);
1870 ghostname = stralloc(s-1);
1872 else if(ch != '\0' && strncmp_const_skip(s - 1, "estimate", s, ch) == 0) {
1873 skip_whitespace(s, ch);
1879 skip_non_whitespace(s, ch);
1881 hostname = stralloc(fp);
1884 skip_whitespace(s, ch);
1892 skip_quoted_string(s, ch);
1894 diskname = unquote_string(qdiskname);
1897 skip_whitespace(s, ch);
1905 skip_non_whitespace(s, ch);
1907 datestamp = stralloc(fp);
1909 skip_whitespace(s, ch);
1911 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1918 skip_integer(s, ch);
1919 if(level < 0 || level > 9) {
1926 skip_whitespace(s, ch);
1928 if(sscanf(s - 1,"[sec %lf nkb %lf ckb %lf kps %lf",
1929 &sec, &nbytes, &cbytes, &kps) != 4) {
1937 dp = lookup_disk(hostname, diskname);
1939 addtoX_summary(&first_failed, &last_failed,
1940 hostname, diskname, level,
1941 _("ERROR [not in disklist]"));
1942 exit_status |= STATUS_FAILED;
1949 repdata = find_repdata(dp, datestamp, level);
1951 repdata->est_nsize = nbytes;
1952 repdata->est_csize = cbytes;
1974 if (curprog == P_DRIVER &&
1975 BSTRNCMP(curstr, "Taper protocol error") == 0) {
1976 exit_status |= STATUS_TAPE;
1978 pidstr = index(curstr,' ');
1982 /* Don't report the pid lines */
1983 if ((!pidstr || BSTRNCMP(pidstr, "pid ") != 0) &&
1984 BSTRNCMP(curstr, "pid-done ") != 0) {
1985 str = vstrallocf(" %s: %s", program_str[curprog], curstr);
1986 addline(¬es, str);
1997 char *s = NULL, *nl;
2000 if(curlog == L_ERROR && curprog == P_TAPER) {
2004 skip_whitespace(s, ch);
2005 if(ch != '\0' && strncmp_const_skip(s - 1, "no-tape", s, ch) == 0) {
2006 skip_whitespace(s, ch);
2008 if((nl = strchr(s - 1, '\n')) != NULL) {
2011 tapestart_error = newstralloc(tapestart_error, s - 1);
2014 exit_status |= STATUS_TAPE;;
2017 /* else some other tape error, handle like other errors */
2019 /* else some other tape error, handle like other errors */
2021 s = vstrallocf(" %s: %s %s", program_str[curprog],
2022 logtype_str[curlog], curstr);
2023 addline(&errsum, s);
2030 handle_summary(void)
2037 static int nb_disk=0;
2042 char *s, *fp, *qdiskname;
2044 char *hostname = NULL, *diskname = NULL;
2046 if(curprog != P_PLANNER && curprog != P_AMFLUSH) {
2052 for(dp = diskq.head; dp != NULL; dp = dp->next)
2060 skip_whitespace(s, ch);
2066 skip_non_whitespace(s, ch);
2068 hostname = newstralloc(hostname, fp);
2071 skip_whitespace(s, ch);
2078 skip_quoted_string(s, ch);
2080 diskname = unquote_string(qdiskname);
2083 dp = lookup_disk(hostname, diskname);
2085 dp = add_disk(&diskq, hostname, diskname);
2093 /* XXX Just a placeholder, in case we decide to do something with L_CHUNK
2094 * log entries. Right now they're just the equivalent of L_SUCCESS, but only
2095 * for a split chunk of the overall dumpfile.
2102 double sec, kps, kbytes;
2107 char *hostname = NULL;
2108 char *diskname = NULL;
2116 if(curprog != P_TAPER) {
2124 skip_whitespace(s, ch);
2130 if (logtype == L_PART || logtype == L_PARTPARTIAL) {
2132 skip_non_whitespace(s, ch);
2134 label = stralloc(fp);
2137 skip_whitespace(s, ch);
2138 if(ch == '\0' || sscanf(s - 1, "%d", &fileno) != 1) {
2143 skip_integer(s, ch);
2144 skip_whitespace(s, ch);
2154 skip_non_whitespace(s, ch);
2156 hostname = stralloc(fp);
2159 skip_whitespace(s, ch);
2166 skip_quoted_string(s, ch);
2168 diskname = unquote_string(fp);
2171 skip_whitespace(s, ch);
2179 skip_non_whitespace(s, ch);
2181 datestamp = stralloc(fp);
2184 skip_whitespace(s, ch);
2185 if(ch == '\0' || sscanf(s - 1, "%d", &chunk) != 1) {
2192 skip_integer(s, ch);
2194 if (ch != '\0' && s[-1] == '/') {
2196 if (sscanf(s - 1, "%d", &totpart) != 1) {
2203 skip_integer(s, ch);
2206 skip_whitespace(s, ch);
2207 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2214 skip_integer(s, ch);
2217 if(level < 0 || level > 9) {
2225 skip_whitespace(s, ch);
2226 if(sscanf(s - 1,"[sec %lf kb %lf kps %lf", &sec, &kbytes, &kps) != 3) {
2235 dp = lookup_disk(hostname, diskname);
2239 str = vstrallocf(_(" %s ERROR [not in disklist]"),
2240 prefix(hostname, diskname, level));
2241 addline(&errsum, str);
2249 repdata = find_repdata(dp, datestamp, level);
2251 sp = &(repdata->taper);
2259 if(current_tape == NULL) {
2260 error("current_tape == NULL");
2262 if (sp->filenum == 0) {
2263 sp->filenum = ++tapefcount;
2264 sp->tapelabel = current_tape->label;
2266 tapechunks[level] +=1;
2267 stats[i].tapechunks +=1;
2268 current_tape->taper_time += sec;
2269 current_tape->coutsize += kbytes;
2270 current_tape->tapechunks += 1;
2281 double kbytes = 0.0;
2282 double origkb = 0.0;
2285 char *s, *fp, *qdiskname;
2287 char *hostname = NULL;
2288 char *diskname = NULL;
2296 if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER &&
2297 curprog != P_CHUNKER) {
2305 skip_whitespace(s, ch);
2311 skip_non_whitespace(s, ch);
2313 hostname = stralloc(fp);
2316 skip_whitespace(s, ch);
2323 skip_quoted_string(s, ch);
2325 diskname = unquote_string(qdiskname);
2327 skip_whitespace(s, ch);
2335 skip_non_whitespace(s, ch);
2337 datestamp = stralloc(fp);
2340 //datestamp is optional
2341 if(strlen(datestamp) < 6) {
2342 totpart = atoi(datestamp);
2343 datestamp = newstralloc(datestamp, run_datestamp);
2346 skip_whitespace(s, ch);
2347 if(ch == '\0' || sscanf(s - 1, "%d", &totpart) != 1) {
2354 skip_integer(s, ch);
2357 skip_whitespace(s, ch);
2359 //totpart is optional
2362 if (*(s-1) == '[') {
2366 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2373 skip_integer(s, ch);
2374 skip_whitespace(s, ch);
2378 if(level < 0 || level > 9) {
2385 /* Planner success messages (for skipped
2386 dumps) do not contain statistics */
2387 if(curprog != P_PLANNER) {
2390 if((curprog != P_DUMPER)
2391 || (sscanf(s - 1,"[sec %lf kb %lf kps %lf orig-kb %lf",
2392 &sec, &kbytes, &kps, &origkb) != 4)) {
2394 if(sscanf(s - 1,"[sec %lf kb %lf kps %lf",
2395 &sec, &kbytes, &kps) != 3) {
2404 if(!isnormal(origkb))
2407 if (curprog == P_TAPER && logtype == L_PARTIAL) {
2408 char *t = index(s-1,']');
2411 errmsg = unquote_string(t+1);
2412 u = vstrallocf(" %s: partial %s: %s",
2413 prefix(hostname, diskname, level),
2414 program_str[curprog], errmsg);
2415 addline(&errsum, u);
2421 dp = lookup_disk(hostname, diskname);
2423 addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
2424 _("ERROR [not in disklist]"));
2425 exit_status |= STATUS_FAILED;
2432 repdata = find_repdata(dp, datestamp, level);
2434 if(curprog == P_PLANNER) {
2435 repdata->dumper.result = L_SKIPPED;
2442 if(curprog == P_TAPER)
2443 sp = &(repdata->taper);
2444 else if(curprog == P_DUMPER)
2445 sp = &(repdata->dumper);
2446 else sp = &(repdata->chunker);
2450 if (origkb < 0.0 && (curprog == P_CHUNKER || curprog == P_TAPER) &&
2451 isnormal(repdata->dumper.outsize)) {
2452 /* take origkb from DUMPER line */
2453 origkb = repdata->dumper.outsize;
2454 } else if (origkb < 0.0) {
2455 /* take origkb from infofile, needed for amflush */
2460 get_info(hostname, diskname, &inf);
2461 tm = localtime(&inf.inf[level].date);
2463 Idatestamp = 10000*(tm->tm_year+1900) +
2464 100*(tm->tm_mon+1) + tm->tm_mday;
2466 Idatestamp = 19000101;
2469 if(atoi(datestamp) == Idatestamp) {
2470 /* grab original size from record */
2471 origkb = (double)inf.inf[level].size;
2477 if (curprog == P_DUMPER &&
2478 (sp->result == L_FAIL || sp->result == L_PARTIAL)) {
2479 addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
2480 _("was successfully retried"));
2487 sp->result = L_SUCCESS;
2488 sp->datestamp = repdata->datestamp;
2491 sp->origsize = origkb;
2492 sp->outsize = kbytes;
2494 if(curprog == P_TAPER) {
2495 if(current_tape == NULL) {
2496 error(_("current_tape == NULL"));
2499 stats[i].taper_time += sec;
2500 sp->filenum = ++tapefcount;
2501 sp->tapelabel = current_tape->label;
2502 sp->totpart = totpart;
2503 tapedisks[level] +=1;
2504 stats[i].tapedisks +=1;
2505 stats[i].tapesize += kbytes;
2506 sp->outsize = kbytes;
2507 if(!isnormal(repdata->chunker.outsize) && isnormal(repdata->dumper.outsize)) { /* dump to tape */
2508 stats[i].outsize += kbytes;
2509 if (abs(kbytes - origkb) >= 32) {
2510 /* server compressed */
2511 stats[i].corigsize += origkb;
2512 stats[i].coutsize += kbytes;
2515 current_tape->tapedisks += 1;
2518 if(curprog == P_DUMPER) {
2519 stats[i].dumper_time += sec;
2520 if (abs(kbytes - origkb) < 32) {
2521 /* not client compressed */
2522 sp->origsize = kbytes;
2525 /* client compressed */
2526 stats[i].corigsize += sp->origsize;
2527 stats[i].coutsize += kbytes;
2529 dumpdisks[level] +=1;
2530 stats[i].dumpdisks +=1;
2531 stats[i].origsize += sp->origsize;
2534 if(curprog == P_CHUNKER) {
2535 sp->outsize = kbytes;
2536 stats[i].outsize += kbytes;
2537 if (abs(kbytes - origkb) >= 32) {
2538 /* server compressed */
2539 stats[i].corigsize += origkb;
2540 stats[i].coutsize += kbytes;
2547 handle_partial(void)
2552 repdata = handle_success(L_PARTIAL);
2556 if(curprog == P_TAPER)
2557 sp = &(repdata->taper);
2558 else if(curprog == P_DUMPER)
2559 sp = &(repdata->dumper);
2560 else sp = &(repdata->chunker);
2562 sp->result = L_PARTIAL;
2566 handle_strange(void)
2569 char *strangestr = NULL;
2573 repdata = handle_success(L_SUCCESS);
2577 qdisk = quote_string(repdata->disk->name);
2579 addline(&strangedet,"");
2580 str = vstrallocf("/-- %s STRANGE",
2581 prefix(repdata->disk->host->hostname, qdisk, repdata->level));
2582 addline(&strangedet, str);
2585 while(contline_next()) {
2587 get_logline(logfile);
2589 if(strncmp_const_skip(curstr, "sendbackup: warning ", s, ch) == 0) {
2590 strangestr = newstralloc(strangestr, s);
2592 addline(&strangedet, curstr);
2594 addline(&strangedet,"\\--------");
2596 str = vstrallocf("STRANGE %s", strangestr? strangestr : _("(see below)"));
2597 addtoX_summary(&first_strange, &last_strange,
2598 repdata->disk->host->hostname, qdisk, repdata->level, str);
2599 exit_status |= STATUS_STRANGE;
2614 char *s, *fp, *qdiskname;
2626 skip_whitespace(s, ch);
2632 skip_non_whitespace(s, ch);
2635 skip_whitespace(s, ch);
2641 skip_quoted_string(s, ch);
2643 diskname = unquote_string(qdiskname);
2645 skip_whitespace(s, ch);
2652 skip_non_whitespace(s, ch);
2654 datestamp = stralloc(fp);
2656 if(strlen(datestamp) < 3) { /* there is no datestamp, it's the level */
2657 level = atoi(datestamp);
2658 datestamp = newstralloc(datestamp, run_datestamp);
2660 else { /* read the level */
2661 skip_whitespace(s, ch);
2662 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2668 skip_integer(s, ch);
2671 skip_whitespace(s, ch);
2679 if((s = strchr(errstr, '\n')) != NULL) {
2683 dp = lookup_disk(hostname, diskname);
2686 addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
2687 _("ERROR [not in disklist]"));
2689 repdata = find_repdata(dp, datestamp, level);
2691 if(curprog == P_TAPER)
2692 sp = &(repdata->taper);
2693 else if (curprog == P_PLANNER)
2694 sp = &(repdata->planner);
2695 else sp = &(repdata->dumper);
2697 if(sp->result != L_SUCCESS)
2698 sp->result = L_FAIL;
2702 if (!((curprog == P_CHUNKER &&
2703 strcmp(errstr, "[dumper returned FAILED]") == 0) ||
2704 (curprog == P_CHUNKER &&
2705 strcmp(errstr, "[Not enough holding disk space]") == 0) ||
2706 (curprog == P_CHUNKER &&
2707 strcmp(errstr, "[cannot read header: got 0 bytes instead of 32768]") == 0))) {
2708 str = vstrallocf(_("FAILED %s"), errstr);
2709 addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
2714 if(curprog == P_DUMPER) {
2715 addline(&errdet,"");
2716 str = vstrallocf("/-- %s FAILED %s",
2717 prefix(hostname, qdiskname, level),
2719 addline(&errdet, str);
2721 while(contline_next()) {
2722 get_logline(logfile);
2723 addline(&errdet, curstr);
2725 addline(&errdet,"\\--------");
2726 exit_status |= STATUS_FAILED;
2733 generate_missing(void)
2738 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2739 if(dp->todo && data(dp) == NULL) {
2740 qdisk = quote_string(dp->name);
2741 addtoX_summary(&first_failed, &last_failed, dp->host->hostname,
2742 qdisk, -987, _("RESULTS MISSING"));
2743 exit_status |= STATUS_MISSING;
2750 generate_bad_estimate(void)
2757 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2759 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
2760 if(repdata->est_csize >= 0.1) {
2761 if(repdata->taper.result == L_SUCCESS ||
2762 repdata->taper.result == L_CHUNKSUCCESS)
2763 outsize = repdata->taper.outsize;
2764 else if(repdata->chunker.result == L_SUCCESS ||
2765 repdata->chunker.result == L_PARTIAL ||
2766 repdata->chunker.result == L_CHUNKSUCCESS)
2767 outsize = repdata->chunker.outsize;
2768 else if(repdata->taper.result == L_PARTIAL)
2769 outsize = repdata->taper.outsize;
2771 outsize = repdata->dumper.outsize;
2773 if( (repdata->est_csize * 0.9 > outsize) && ( repdata->est_csize - outsize > 1.0e5 ) ) {
2775 _(" big estimate: %s %s %d"),
2776 repdata->disk->host->hostname,
2777 repdata->disk->name,
2782 _(" est: %.0lf%s out %.0lf%s"),
2783 du(repdata->est_csize), displayunit,
2784 du(outsize), displayunit);
2788 else if( (repdata->est_csize * 1.1 < outsize) && (outsize - repdata->est_csize > 1.0e5 ) ) {
2790 _(" small estimate: %s %s %d"),
2791 repdata->disk->host->hostname,
2792 repdata->disk->name,
2797 _(" est: %.0lf%s out %.0lf%s"),
2798 du(repdata->est_csize), displayunit,
2799 du(outsize), displayunit);
2815 static char *str = NULL;
2817 if (level == -987) {
2818 str = newvstrallocf(str, " %s %s",
2819 host ? host : _("(host?)"),
2820 disk ? disk : _("(disk?)"));
2822 str = newvstrallocf(str, " %s %s lev %d",
2823 host ? host : _("(host?)"),
2824 disk ? disk : _("(disk?)"),
2841 static char *str = NULL;
2843 h=alloc(len_host+1);
2845 strncpy(h, host, len_host);
2847 strncpy(h, _("(host?)"), len_host);
2850 for(l = strlen(h); l < len_host; l++) {
2853 d=alloc(len_disk+1);
2855 strncpy(d, disk, len_disk);
2857 strncpy(d, _("(disk?)"), len_disk);
2860 for(l = strlen(d); l < len_disk; l++) {
2863 if (level == -987) {
2864 str = newvstrallocf(str, " %s %s", h, d);
2866 str = newvstrallocf(str, " %s %s lev %d", h, d, level);
2876 X_summary_t **first,
2883 X_summary_t *X_summary;
2885 X_summary = alloc(SIZEOF(X_summary_t));
2886 X_summary->hostname = stralloc(host);
2887 X_summary->diskname = stralloc(disk);
2888 X_summary->level = level;
2889 X_summary->str = stralloc(str);
2890 X_summary->next = NULL;
2891 if (*first == NULL) {
2895 (*last)->next = X_summary;
2908 lbl_templ = config_dir_relative(lbl_templ);
2909 if ((fd = open(lbl_templ, 0)) < 0) {
2911 curprog = P_REPORTER;
2912 curstr = vstrallocf(_("could not open PostScript template file %s: %s"),
2913 lbl_templ, strerror(errno));
2917 afclose(postscript);
2920 while ((numread = read(fd, buf, SIZEOF(buf))) > 0) {
2921 if (fwrite(buf, (size_t)numread, 1, postscript) != 1) {
2923 curprog = P_REPORTER;
2924 curstr = vstrallocf(_("error copying PostScript template file %s: %s"),
2925 lbl_templ, strerror(errno));
2929 afclose(postscript);
2935 curprog = P_REPORTER;
2936 curstr = vstrallocf(_("error reading PostScript template file %s: %s"),
2937 lbl_templ, strerror(errno));
2941 afclose(postscript);
2950 /*@keep@*/ disk_t *dp,
2954 repdata_t *repdata, *prev;
2957 datestamp = run_datestamp;
2959 for(repdata = data(dp); repdata != NULL && (repdata->level != level || strcmp(repdata->datestamp,datestamp)!=0); repdata = repdata->next) {
2963 repdata = (repdata_t *)alloc(SIZEOF(repdata_t));
2964 memset(repdata, '\0', SIZEOF(repdata_t));
2966 repdata->datestamp = stralloc(datestamp ? datestamp : "");
2967 repdata->level = level;
2968 repdata->dumper.result = L_BOGUS;
2969 repdata->taper.result = L_BOGUS;
2970 repdata->next = NULL;
2972 prev->next = repdata;
2974 dp->up = (void *)repdata;
2981 do_postscript_output(void)
2983 tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
2986 double outsize, origsize;
2993 tapesize = tapetype_get_length(tp);
2994 marksize = tapetype_get_filemark(tp);
2996 for(current_tape = stats_by_tape; current_tape != NULL;
2997 current_tape = current_tape->next) {
2999 if (current_tape->label == NULL) {
3003 copy_template_file(tapetype_get_lbl_templ(tp));
3005 if (postscript == NULL)
3008 /* generate a few elements */
3009 g_fprintf(postscript,"(%s) DrawDate\n\n",
3010 nicedate(run_datestamp ? run_datestamp : "0"));
3011 g_fprintf(postscript,_("(Amanda Version %s) DrawVers\n"),version());
3012 g_fprintf(postscript,"(%s) DrawTitle\n", current_tape->label);
3015 g_fprintf(postscript, "(Total Size: %6.1lf MB) DrawStat\n",
3016 mb(current_tape->coutsize));
3017 g_fprintf(postscript, _("(Tape Used (%%) "));
3018 divzero(postscript, pct(current_tape->coutsize +
3019 marksize * (current_tape->tapedisks + current_tape->tapechunks)),
3021 g_fprintf(postscript," %%) DrawStat\n");
3022 g_fprintf(postscript, _("(Compression Ratio: "));
3023 divzero(postscript, pct(current_tape->coutsize),current_tape->corigsize);
3024 g_fprintf(postscript," %%) DrawStat\n");
3025 g_fprintf(postscript,_("(Filesystems Taped: %4d) DrawStat\n"),
3026 current_tape->tapedisks);
3030 g_fprintf(postscript,
3031 "(-) (%s) (-) ( 0) ( 32) ( 32) DrawHost\n",
3032 current_tape->label);
3034 for(dp = sortq.head; dp != NULL; dp = dp->next) {
3035 if (dp->todo == 0) {
3038 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
3040 if(repdata->taper.tapelabel != current_tape->label) {
3044 if(repdata->dumper.result == L_SUCCESS ||
3045 repdata->dumper.result == L_PARTIAL)
3046 origsize = repdata->dumper.origsize;
3048 origsize = repdata->taper.origsize;
3050 if(repdata->taper.result == L_SUCCESS ||
3051 repdata->taper.result == L_PARTIAL)
3052 outsize = repdata->taper.outsize;
3054 outsize = repdata->dumper.outsize;
3056 if (repdata->taper.result == L_SUCCESS ||
3057 repdata->taper.result == L_PARTIAL) {
3058 if(isnormal(origsize)) {
3059 g_fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8.0lf) (%8.0lf) DrawHost\n",
3060 dp->host->hostname, dp->name, repdata->level,
3061 repdata->taper.filenum, origsize,
3065 g_fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8s) (%8.0lf) DrawHost\n",
3066 dp->host->hostname, dp->name, repdata->level,
3067 repdata->taper.filenum, "",
3074 g_fprintf(postscript,"\nshowpage\n");