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 {
70 typedef struct timedata_s {
72 double origsize, outsize;
80 typedef struct repdata_s {
83 double est_nsize, est_csize;
88 struct repdata_s *next;
91 #define data(dp) ((repdata_t *)(dp)->up)
93 static struct cumulative_stats {
94 int dumpdisks, tapedisks, tapechunks;
95 double taper_time, dumper_time;
96 double outsize, origsize, tapesize;
97 double coutsize, corigsize; /* compressed dump only */
100 static int dumpdisks[10], tapedisks[10], tapechunks[10]; /* by-level breakdown of disk count */
102 typedef struct taper_s {
105 double coutsize, corigsize;
106 int tapedisks, tapechunks;
107 struct taper_s *next;
110 static taper_t *stats_by_tape = NULL;
111 static taper_t *current_tape = NULL;
113 typedef struct X_summary_s {
118 struct X_summary_s *next;
121 static X_summary_t *first_strange=NULL, *last_strange=NULL;
122 static X_summary_t *first_failed=NULL, *last_failed=NULL;
124 static double total_time, startup_time, planner_time;
126 /* count files to tape */
127 static int tapefcount = 0;
129 static int exit_status = 0;
130 static char *run_datestamp;
131 static char *tape_labels = NULL;
132 static int last_run_tapes = 0;
133 static int degraded_mode = 0; /* defined in driverio too */
134 static int normal_run = 0;
135 static int amflush_run = 0;
136 static int got_finish = 0;
137 static int cmdlogfname = 0;
138 static char *ghostname = NULL;
140 static char *tapestart_error = NULL;
142 static FILE *logfile, *mailf;
144 static FILE *postscript;
145 static char *printer;
147 static disklist_t diskq;
148 static disklist_t sortq;
150 static line_t *errsum = NULL;
151 static line_t *errdet = NULL;
152 static line_t *strangedet = NULL;
153 static line_t *notes = NULL;
155 static char MaxWidthsRequested = 0; /* determined via config data */
157 static char *displayunit;
158 static long int unitdivisor;
160 /* local functions */
161 int main(int argc, char **argv);
163 static char * nicedate(const char * datestamp);
164 static char * prefix(char *host, char *disk, int level);
165 static char * prefixstrange(char *host, char *disk, int level,
166 size_t len_host, size_t len_disk);
167 static char * Rule(int From, int To);
168 static char * sDivZero(double a, double b, int cn);
169 static char * TextRule(int From, int To, char *s);
170 static int ColWidth(int From, int To);
171 static int contline_next(void);
172 static int sort_by_name(disk_t *a, disk_t *b);
173 static repdata_t *find_repdata(disk_t *dp, char *datestamp, int level);
174 static repdata_t *handle_chunk(logtype_t logtype);
175 static repdata_t *handle_success(logtype_t logtype);
176 static void addline(line_t **lp, char *str);
177 static void addtoX_summary(X_summary_t **first, X_summary_t **last,
178 char *host, char *disk, int level, char *str);
179 static void bogus_line(const char *);
180 static void CalcMaxWidth(void);
181 static void CheckFloatMax(ColumnInfo *cd, double d);
182 static void CheckIntMax(ColumnInfo *cd, int n);
183 static void CheckStringMax(ColumnInfo *cd, char *s);
184 static void copy_template_file(char *lbl_templ);
185 static void do_postscript_output(void);
186 static void generate_missing(void);
187 static void generate_bad_estimate(void);
188 static void handle_disk(void);
189 static void handle_error(void);
190 static void handle_failed(void);
191 static void handle_finish(void);
192 static void handle_note(void);
193 static void handle_partial(void);
194 static void handle_start(void);
195 static void handle_stats(void);
196 static void handle_strange(void);
197 static void handle_summary(void);
198 static void output_lines(line_t *lp, FILE *f);
199 static void output_stats(void);
200 static void output_X_summary(X_summary_t *first);
201 static void output_summary(void);
202 static void output_tapeinfo(void);
203 static void sort_disks(void);
204 static void usage(void);
212 for (i=From; i<=To && ColumnData[i].Name != NULL; i++) {
213 Width+= ColumnData[i].PrefixSpace + ColumnData[i].Width;
224 int Leng= ColWidth(0, ColumnDataCount());
225 char *RuleSpace= alloc((size_t)(Leng+1));
226 ThisLeng= ColWidth(From, To);
227 for (i=0;i<ColumnData[From].PrefixSpace; i++)
229 for (; i<ThisLeng; i++)
231 RuleSpace[ThisLeng]= '\0';
241 ColumnInfo *cd= &ColumnData[From];
243 int nbrules, i, txtlength;
244 int RuleSpaceSize= ColWidth(0, ColumnDataCount());
245 char *RuleSpace= alloc((size_t)RuleSpaceSize), *tmp;
247 leng = (int)strlen(s);
248 if(leng >= (RuleSpaceSize - cd->PrefixSpace))
249 leng = RuleSpaceSize - cd->PrefixSpace - 1;
250 g_snprintf(RuleSpace, (size_t)RuleSpaceSize, "%*s%*.*s ", cd->PrefixSpace, "",
252 txtlength = cd->PrefixSpace + leng + 1;
253 nbrules = ColWidth(From,To) - txtlength;
254 for(tmp=RuleSpace + txtlength, i=nbrules ; i>0; tmp++,i--)
266 ColumnInfo *cd= &ColumnData[cn];
267 static char PrtBuf[256];
269 g_snprintf(PrtBuf, SIZEOF(PrtBuf),
270 "%*s", cd->Width, "-- ");
272 g_snprintf(PrtBuf, SIZEOF(PrtBuf),
273 cd->Format, cd->Width, cd->Precision, a/b);
282 if ((ch = getc(logfile)) != EOF) {
283 if (ungetc(ch, logfile) == EOF) {
284 if (ferror(logfile)) {
285 error(_("ungetc failed: %s\n"), strerror(errno));
288 error(_("ungetc failed: EOF\n"));
302 /* allocate new line node */
303 new = (line_t *) alloc(SIZEOF(line_t));
305 new->str = stralloc(str);
307 /* add to end of list */
312 while (p->next != NULL)
321 error(_("Usage: amreport conf [-i] [-M address] [-f output-file] [-l logfile] [-p postscript-file] [-o configoption]*"));
333 char *logfname, *psfname, *outfname, *subj_str = NULL;
336 char *mail_cmd = NULL, *printer_cmd = NULL;
339 char *ColumnSpec = "";
344 char *lbl_templ = NULL;
345 config_overwrites_t *cfg_ovr = NULL;
346 char *cfg_opt = NULL;
349 * Configure program for internationalization:
350 * 1) Only set the message locale for now.
351 * 2) Set textdomain for all amanda related programs to "amanda"
352 * We don't want to be forced to support dozens of message catalogs.
354 setlocale(LC_MESSAGES, "C");
355 textdomain("amanda");
359 set_pname("amreport");
361 dbopen(DBG_SUBDIR_SERVER);
363 /* Don't die when child closes pipe */
364 signal(SIGPIPE, SIG_IGN);
366 malloc_size_1 = malloc_inuse(&malloc_hist_1);
368 /* Process options */
370 erroutput_type = ERR_INTERACTIVE;
376 cwd = g_get_current_dir();
378 error(_("Cannot determine current working directory: %s"),
384 if (argv[1][0] == '-') {
389 /* get the config name and move past it */
393 cfg_ovr = new_config_overwrites(argc/2);
394 while((opt = getopt(argc, argv, "o:M:f:l:p:i")) != EOF) {
400 if (mailto != NULL) {
401 error(_("you may specify at most one -M"));
404 mailto = stralloc(optarg);
405 if(!validate_mailto(mailto)) {
406 error(_("mail address has invalid characters"));
411 if (outfname != NULL) {
412 error(_("you may specify at most one -f"));
415 if (*optarg == '/') {
416 outfname = stralloc(optarg);
418 outfname = vstralloc(cwd, "/", optarg, NULL);
423 if (logfname != NULL) {
424 error(_("you may specify at most one -l"));
427 if (*optarg == '/') {
428 logfname = stralloc(optarg);
430 logfname = vstralloc(cwd, "/", optarg, NULL);
434 if (psfname != NULL) {
435 error(_("you may specify at most one -p"));
438 if (*optarg == '/') {
439 psfname = stralloc(optarg);
441 psfname = vstralloc(cwd, "/", optarg, NULL);
445 add_config_overwrite_opt(cfg_ovr, optarg);
458 if( !mailout && mailto ){
459 g_printf(_("You cannot specify both -i & -M at the same time\n"));
467 g_printf(_("You must run amreport with '-f <output file>' because configure\n"));
468 g_printf(_("didn't find a mailer.\n"));
473 /* read configuration files */
475 /* ignore any errors reading the config file (amreport can run without a config) */
476 config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
477 if (cfg_ovr) apply_config_overwrites(cfg_ovr);
479 check_running_as(RUNNING_AS_DUMPUSER);
481 dbrename(config_name, DBG_SUBDIR_SERVER);
483 safe_cd(); /* must be called *after* config_init() */
485 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
486 /* Ignore error from read_diskfile */
487 read_diskfile(conf_diskfile, &diskq);
488 amfree(conf_diskfile);
489 if(mailout && !mailto &&
490 getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0) {
491 mailto = getconf_str(CNF_MAILTO);
492 if(!validate_mailto(mailto)){
497 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
498 /* Ignore error from read_tapelist */
499 read_tapelist(conf_tapelist);
500 amfree(conf_tapelist);
501 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
502 if(open_infofile(conf_infofile)) {
503 error(_("could not open info db \"%s\""), conf_infofile);
506 amfree(conf_infofile);
508 displayunit = getconf_str(CNF_DISPLAYUNIT);
509 unitdivisor = getconf_unit_divisor();
511 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
512 if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
514 curprog = P_REPORTER;
519 ColumnSpec = ""; /* use the default */
520 if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
522 curprog = P_REPORTER;
529 for (cn = 0; ColumnData[cn].Name != NULL; cn++) {
530 if (ColumnData[cn].MaxWidth) {
531 MaxWidthsRequested = 1;
539 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
540 logfname = vstralloc(conf_logdir, "/", "log", NULL);
544 if((logfile = fopen(logfname, "r")) == NULL) {
546 curprog = P_REPORTER;
547 curstr = vstralloc(_("could not open log "),
556 while(logfile && get_logline(logfile)) {
558 case L_START: handle_start(); break;
559 case L_FINISH: handle_finish(); break;
561 case L_INFO: handle_note(); break;
562 case L_WARNING: handle_note(); break;
564 case L_SUMMARY: handle_summary(); break;
565 case L_STATS: handle_stats(); break;
567 case L_ERROR: handle_error(); break;
568 case L_FATAL: handle_error(); break;
570 case L_DISK: handle_disk(); break;
572 case L_DONE: handle_success(curlog); break;
573 case L_SUCCESS: handle_success(curlog); break;
574 case L_CHUNKSUCCESS: handle_success(curlog); break;
575 case L_PART: handle_chunk(curlog); break;
576 case L_PARTPARTIAL: handle_chunk(curlog); break;
577 case L_CHUNK: handle_chunk(curlog); break;
578 case L_PARTIAL: handle_partial(); break;
579 case L_STRANGE: handle_strange(); break;
580 case L_FAIL: handle_failed(); break;
584 curprog = P_REPORTER;
585 curstr = vstrallocf(_("unexpected log line: %s"), curstr);
594 generate_bad_estimate();
597 subj_str = vstralloc(getconf_str(CNF_ORG),
598 " ", amflush_run ? "AMFLUSH" : "AMANDA",
599 " ", "MAIL REPORT FOR",
600 " ", nicedate(run_datestamp ? run_datestamp : "0"),
603 /* lookup the tapetype and printer type from the amanda.conf file. */
604 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
605 printer = getconf_str(CNF_PRINTER);
607 /* ignore SIGPIPE so if a child process dies we do not also go away */
608 signal(SIGPIPE, SIG_IGN);
610 /* open pipe to mailer */
613 /* output to a file */
614 if((mailf = fopen(outfname,"w")) == NULL) {
615 error(_("could not open output file: %s %s"), outfname, strerror(errno));
618 if (mailto != NULL) {
619 g_fprintf(mailf, "To: %s\n", mailto);
620 g_fprintf(mailf, "Subject: %s\n\n", subj_str);
626 mail_cmd = vstralloc(MAILER,
627 " -s", " \"", subj_str, "\"",
629 if((mailf = popen(mail_cmd, "w")) == NULL) {
630 error(_("could not open pipe to \"%s\": %s"),
631 mail_cmd, strerror(errno));
637 g_printf(_("No mail sent! "));
638 g_printf(_("No valid mail address has been specified in amanda.conf or on the commmand line\n"));
645 /* open pipe to print spooler if necessary) */
648 /* if the postscript_label_template (tp->lbl_templ) field is not */
649 /* the empty string (i.e. it is set to something), open the */
650 /* postscript debugging file for writing. */
652 lbl_templ = tapetype_get_lbl_templ(tp);
653 if (tp && lbl_templ && strcmp(lbl_templ, "") != 0) {
654 if ((postscript = fopen(psfname, "w")) == NULL) {
656 curprog = P_REPORTER;
657 curstr = vstrallocf(_("could not open %s: %s"),
666 if (strcmp(printer, "") != 0) /* alternate printer is defined */
667 /* print to the specified printer */
669 printer_cmd = vstralloc(LPRCMD, " ", LPRFLAG, printer, NULL);
671 printer_cmd = vstralloc(LPRCMD, NULL);
674 /* print to the default printer */
675 printer_cmd = vstralloc(LPRCMD, NULL);
678 lbl_templ = tapetype_get_lbl_templ(tp);
679 if (tp && lbl_templ && strcmp(lbl_templ, "") != 0) {
681 if ((postscript = popen(printer_cmd, "w")) == NULL) {
683 curprog = P_REPORTER;
684 curstr = vstrallocf(_("could not open pipe to %s: %s"),
685 printer_cmd, strerror(errno));
691 curprog = P_REPORTER;
692 curstr = vstrallocf(_("no printer command defined"));
705 if(!got_finish) fputs(_("*** THE DUMPS DID NOT FINISH PROPERLY!\n\n"), mailf);
708 g_fprintf(mailf, _("Hostname: %s\n"), ghostname);
709 g_fprintf(mailf, _("Org : %s\n"), getconf_str(CNF_ORG));
710 g_fprintf(mailf, _("Config : %s\n"), config_name);
711 g_fprintf(mailf, _("Date : %s\n"),
712 nicedate(run_datestamp ? run_datestamp : "0"));
713 g_fprintf(mailf,"\n");
718 if(first_failed || errsum) {
719 g_fprintf(mailf,_("\nFAILURE DUMP SUMMARY:\n"));
720 if(first_failed) output_X_summary(first_failed);
721 if(errsum) output_lines(errsum, mailf);
724 g_fprintf(mailf,_("\nSTRANGE DUMP SUMMARY:\n"));
725 if(first_strange) output_X_summary(first_strange);
727 fputs("\n\n", mailf);
732 g_fprintf(mailf,"\n\f\n");
733 g_fprintf(mailf,_("FAILED DUMP DETAILS:\n"));
734 output_lines(errdet, mailf);
737 g_fprintf(mailf,"\n\f\n");
738 g_fprintf(mailf,_("STRANGE DUMP DETAILS:\n"));
739 output_lines(strangedet, mailf);
742 g_fprintf(mailf,"\n\f\n");
743 g_fprintf(mailf,_("NOTES:\n"));
744 output_lines(notes, mailf);
746 if(sortq.head != NULL) {
747 g_fprintf(mailf,"\n\f\n");
748 g_fprintf(mailf,_("DUMP SUMMARY:\n"));
751 g_fprintf(mailf,_("\n(brought to you by Amanda version %s)\n"),
756 do_postscript_output();
760 /* close postscript file */
761 if (psfname && postscript) {
762 /* it may be that postscript is NOT opened */
766 if (postscript != NULL && pclose(postscript) != 0) {
767 error(_("printer command failed: %s"), printer_cmd);
773 /* close output file */
779 if((exitcode = pclose(mailf)) != 0) {
780 char *exitstr = str_exit_status("mail command", exitcode);
788 free_disklist(&diskq);
789 amfree(run_datestamp);
801 #define mb(f) ((f)/1024) /* kbytes -> mbutes */
802 #define du(f) ((f)/unitdivisor) /* kbytes -> displayunit */
803 #define pct(f) ((f)*100.0) /* percent */
804 #define hrmn(f) ((int)(f)+30)/3600, (((int)(f)+30)%3600)/60
805 #define mnsc(f) ((int)(f+0.5))/60, ((int)(f+0.5)) % 60
807 #define divzero(fp,a,b) \
811 g_fprintf((fp)," -- "); \
812 else if ((q = (a)/q) >= 999.95) \
813 g_fprintf((fp), "###.#"); \
815 g_fprintf((fp), "%5.1lf",q); \
817 #define divzero_wide(fp,a,b) \
821 g_fprintf((fp)," -- "); \
822 else if ((q = (a)/q) >= 99999.95) \
823 g_fprintf((fp), "#####.#"); \
825 g_fprintf((fp), "%7.1lf",q); \
832 tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
838 tapesize = tapetype_get_length(tp);
839 marksize = tapetype_get_filemark(tp);
841 tapesize = 100 * 1024 * 1024;
842 marksize = 1 * 1024 * 1024;
845 stats[2].dumpdisks = stats[0].dumpdisks + stats[1].dumpdisks;
846 stats[2].tapedisks = stats[0].tapedisks + stats[1].tapedisks;
847 stats[2].tapechunks = stats[0].tapechunks + stats[1].tapechunks;
848 stats[2].outsize = stats[0].outsize + stats[1].outsize;
849 stats[2].origsize = stats[0].origsize + stats[1].origsize;
850 stats[2].tapesize = stats[0].tapesize + stats[1].tapesize;
851 stats[2].coutsize = stats[0].coutsize + stats[1].coutsize;
852 stats[2].corigsize = stats[0].corigsize + stats[1].corigsize;
853 stats[2].taper_time = stats[0].taper_time + stats[1].taper_time;
854 stats[2].dumper_time = stats[0].dumper_time + stats[1].dumper_time;
856 if(!got_finish) /* no driver finish line, estimate total run time */
857 total_time = stats[2].taper_time + planner_time;
859 idle_time = (total_time - startup_time) - stats[2].taper_time;
860 if(idle_time < 0) idle_time = 0.0;
862 g_fprintf(mailf,_("STATISTICS:\n"));
864 _(" Total Full Incr.\n"));
866 _(" -------- -------- --------\n"));
869 _("Estimate Time (hrs:min) %2d:%02d\n"), hrmn(planner_time));
872 _("Run Time (hrs:min) %2d:%02d\n"), hrmn(total_time));
875 _("Dump Time (hrs:min) %2d:%02d %2d:%02d %2d:%02d\n"),
876 hrmn(stats[2].dumper_time), hrmn(stats[0].dumper_time),
877 hrmn(stats[1].dumper_time));
880 _("Output Size (meg) %8.1lf %8.1lf %8.1lf\n"),
881 mb(stats[2].outsize), mb(stats[0].outsize), mb(stats[1].outsize));
884 _("Original Size (meg) %8.1lf %8.1lf %8.1lf\n"),
885 mb(stats[2].origsize), mb(stats[0].origsize),
886 mb(stats[1].origsize));
888 g_fprintf(mailf, _("Avg Compressed Size (%%) "));
889 divzero(mailf, pct(stats[2].coutsize),stats[2].corigsize);
890 fputs(_(" "), mailf);
891 divzero(mailf, pct(stats[0].coutsize),stats[0].corigsize);
892 fputs(_(" "), mailf);
893 divzero(mailf, pct(stats[1].coutsize),stats[1].corigsize);
895 if(stats[1].dumpdisks > 0) fputs(_(" (level:#disks ...)"), mailf);
899 _("Filesystems Dumped %4d %4d %4d"),
900 stats[2].dumpdisks, stats[0].dumpdisks, stats[1].dumpdisks);
902 if(stats[1].dumpdisks > 0) {
904 for(lv = 1; lv < 10; lv++) if(dumpdisks[lv]) {
905 fputs(first?_(" ("):_(" "), mailf);
907 g_fprintf(mailf, _("%d:%d"), lv, dumpdisks[lv]);
913 g_fprintf(mailf, _("Avg Dump Rate (k/s) "));
914 divzero_wide(mailf, stats[2].outsize,stats[2].dumper_time);
915 fputs(_(" "), mailf);
916 divzero_wide(mailf, stats[0].outsize,stats[0].dumper_time);
917 fputs(_(" "), mailf);
918 divzero_wide(mailf, stats[1].outsize,stats[1].dumper_time);
923 _("Tape Time (hrs:min) %2d:%02d %2d:%02d %2d:%02d\n"),
924 hrmn(stats[2].taper_time), hrmn(stats[0].taper_time),
925 hrmn(stats[1].taper_time));
928 _("Tape Size (meg) %8.1lf %8.1lf %8.1lf\n"),
929 mb(stats[2].tapesize), mb(stats[0].tapesize),
930 mb(stats[1].tapesize));
932 g_fprintf(mailf, _("Tape Used (%%) "));
933 divzero(mailf, pct(stats[2].tapesize+marksize*(stats[2].tapedisks+stats[2].tapechunks)),(double)tapesize);
934 fputs(_(" "), mailf);
935 divzero(mailf, pct(stats[0].tapesize+marksize*(stats[0].tapedisks+stats[0].tapechunks)),(double)tapesize);
936 fputs(_(" "), mailf);
937 divzero(mailf, pct(stats[1].tapesize+marksize*(stats[1].tapedisks+stats[1].tapechunks)),(double)tapesize);
939 if(stats[1].tapedisks > 0) fputs(_(" (level:#disks ...)"), mailf);
943 _("Filesystems Taped %4d %4d %4d"),
944 stats[2].tapedisks, stats[0].tapedisks, stats[1].tapedisks);
946 if(stats[1].tapedisks > 0) {
948 for(lv = 1; lv < 10; lv++) if(tapedisks[lv]) {
949 fputs(first?_(" ("):_(" "), mailf);
951 g_fprintf(mailf, _("%d:%d"), lv, tapedisks[lv]);
957 if(stats[1].tapechunks > 0) fputs(_(" (level:#chunks ...)"), mailf);
961 _("Chunks Taped %4d %4d %4d"),
962 stats[2].tapechunks, stats[0].tapechunks, stats[1].tapechunks);
964 if(stats[1].tapechunks > 0) {
966 for(lv = 1; lv < 10; lv++) if(tapechunks[lv]) {
967 fputs(first?_(" ("):_(" "), mailf);
969 g_fprintf(mailf, _("%d:%d"), lv, tapechunks[lv]);
975 g_fprintf(mailf, _("Avg Tp Write Rate (k/s) "));
976 divzero_wide(mailf, stats[2].tapesize,stats[2].taper_time);
977 fputs(_(" "), mailf);
978 divzero_wide(mailf, stats[0].tapesize,stats[0].taper_time);
979 fputs(_(" "), mailf);
980 divzero_wide(mailf, stats[1].tapesize,stats[1].taper_time);
984 int label_length = (int)strlen(stats_by_tape->label) + 5;
985 g_fprintf(mailf,_("\nUSAGE BY TAPE:\n"));
986 g_fprintf(mailf,_(" %-*s Time Size %% Nb Nc\n"),
987 label_length, _("Label"));
988 for(current_tape = stats_by_tape; current_tape != NULL;
989 current_tape = current_tape->next) {
990 g_fprintf(mailf, _(" %-*s"), label_length, current_tape->label);
991 g_fprintf(mailf, _(" %2d:%02d"), hrmn(current_tape->taper_time));
992 g_fprintf(mailf, _(" %8.0lf%s "), du(current_tape->coutsize), displayunit);
993 divzero(mailf, pct(current_tape->coutsize + marksize *
994 (current_tape->tapedisks+current_tape->tapechunks)),
996 g_fprintf(mailf, _(" %4d"), current_tape->tapedisks);
997 g_fprintf(mailf, _(" %4d\n"), current_tape->tapechunks);
1005 output_tapeinfo(void)
1012 if (last_run_tapes > 0) {
1015 plural(_("The dumps were flushed to tape %s.\n"),
1016 _("The dumps were flushed to tapes %s.\n"),
1018 tape_labels ? tape_labels : "");
1021 plural(_("These dumps were to tape %s.\n"),
1022 _("These dumps were to tapes %s.\n"),
1024 tape_labels ? tape_labels : "");
1029 _("*** A TAPE ERROR OCCURRED: %s.\n"), tapestart_error);
1031 if (cmdlogfname == 1) {
1033 fputs(_("Some dumps may have been left in the holding disk.\n"),
1035 g_fprintf(mailf,"\n");
1038 GSList *holding_list, *holding_file;
1039 off_t h_size = 0, mh_size;
1041 holding_list = holding_get_files_for_flush(NULL);
1042 for(holding_file=holding_list; holding_file != NULL;
1043 holding_file = holding_file->next) {
1044 mh_size = holding_file_size((char *)holding_file->data, 1);
1051 _("There are %lld%s of dumps left in the holding disk.\n"),
1052 (long long)h_size, displayunit);
1053 if (getconf_boolean(CNF_AUTOFLUSH)) {
1054 g_fprintf(mailf, _("They will be flushed on the next run.\n"));
1056 g_fprintf(mailf, _("Run amflush to flush them to tape.\n"));
1058 g_fprintf(mailf,"\n");
1059 } else if (degraded_mode) {
1060 g_fprintf(mailf, _("No dumps are left in the holding disk. %lld%s\n"), (long long)h_size, displayunit);
1061 g_fprintf(mailf,"\n");
1065 tp = lookup_last_reusable_tape(skip);
1067 run_tapes = getconf_int(CNF_RUNTAPES);
1070 fputs(_("The next tape Amanda expects to use is: "), mailf);
1071 else if(run_tapes > 1)
1072 g_fprintf(mailf, _("The next %d tapes Amanda expects to use are: "),
1076 for (i=0 ; i < run_tapes ; i++) {
1078 if (nb_new_tape > 0) {
1079 if (nb_new_tape == 1)
1080 g_fprintf(mailf, _("1 new tape, "));
1082 g_fprintf(mailf, _("%d new tapes, "), nb_new_tape);
1085 g_fprintf(mailf, "%s", tp->label);
1086 if (i < run_tapes-1) fputs(", ", mailf);
1092 tp = lookup_last_reusable_tape(skip);
1094 if (nb_new_tape > 0) {
1095 if (nb_new_tape == 1)
1096 g_fprintf(mailf, _("1 new tape"));
1098 g_fprintf(mailf, _("%d new tapes"), nb_new_tape);
1100 fputs(".\n", mailf);
1102 run_tapes = getconf_int(CNF_RUNTAPES);
1103 print_new_tapes(mailf, run_tapes);
1111 size_t len_host=0, len_disk=0;
1112 X_summary_t *strange;
1115 for(strange=first; strange != NULL; strange = strange->next) {
1116 if(strlen(strange->hostname) > len_host)
1117 len_host = strlen(strange->hostname);
1118 if(strlen(strange->diskname) > len_disk)
1119 len_disk = strlen(strange->diskname);
1121 for(strange=first; strange != NULL; strange = strange->next) {
1122 str = vstralloc(" ", prefixstrange(strange->hostname, strange->diskname, strange->level, len_host, len_disk),
1123 " ", strange->str, NULL);
1124 g_fprintf(mailf, "%s\n", str);
1155 rc = strcmp(a->host->hostname, b->host->hostname);
1156 if(rc == 0) rc = strcmp(a->name, b->name);
1165 sortq.head = sortq.tail = NULL;
1166 while(!empty(diskq)) {
1167 dp = dequeue_disk(&diskq);
1168 if(data(dp) == NULL) { /* create one */
1169 find_repdata(dp, run_datestamp, 0);
1171 insert_disk(&sortq, dp, sort_by_name);
1181 int l = (int)strlen(s);
1197 g_snprintf(testBuf, SIZEOF(testBuf),
1198 cd->Format, cd->Width, cd->Precision, n);
1199 l = (int)strlen(testBuf);
1214 g_snprintf(testBuf, SIZEOF(testBuf),
1215 cd->Format, cd->Width, cd->Precision, d);
1216 l = (int)strlen(testBuf);
1222 static int HostName;
1227 static int Compress;
1228 static int DumpTime;
1229 static int DumpRate;
1230 static int TapeTime;
1231 static int TapeRate;
1236 /* we have to look for columspec's, that require the recalculation.
1237 * we do here the same loops over the sortq as is done in
1238 * output_summary. So, if anything is changed there, we have to
1239 * change this here also.
1248 for (i=0;ColumnData[i].Name != NULL; i++) {
1249 if (ColumnData[i].MaxWidth) {
1250 l = (int)strlen(ColumnData[i].Title);
1251 if (ColumnData[i].Width < l)
1252 ColumnData[i].Width= l;
1256 for(dp = sortq.head; dp != NULL; dp = dp->next) {
1258 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1259 char TimeRateBuffer[40];
1261 CheckStringMax(&ColumnData[HostName], dp->host->hostname);
1262 qdevname = quote_string(dp->name);
1263 CheckStringMax(&ColumnData[Disk], qdevname);
1265 if (repdata->dumper.result == L_BOGUS &&
1266 repdata->taper.result == L_BOGUS)
1268 CheckIntMax(&ColumnData[Level], repdata->level);
1269 if(repdata->dumper.result == L_SUCCESS ||
1270 repdata->dumper.result == L_CHUNKSUCCESS) {
1271 CheckFloatMax(&ColumnData[OrigKB],
1272 (double)du(repdata->dumper.origsize));
1273 CheckFloatMax(&ColumnData[OutKB],
1274 (double)du(repdata->dumper.outsize));
1275 if(abs(repdata->dumper.outsize - repdata->dumper.origsize)< 32)
1278 f = repdata->dumper.origsize;
1279 CheckStringMax(&ColumnData[Compress],
1280 sDivZero(pct(repdata->dumper.outsize), f, Compress));
1283 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1284 "%3d:%02d", mnsc(repdata->dumper.sec));
1286 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1288 CheckStringMax(&ColumnData[DumpTime], TimeRateBuffer);
1290 CheckFloatMax(&ColumnData[DumpRate], repdata->dumper.kps);
1293 if(repdata->taper.result == L_FAIL) {
1294 CheckStringMax(&ColumnData[TapeTime], "FAILED");
1297 if(repdata->taper.result == L_SUCCESS ||
1298 repdata->taper.result == L_CHUNKSUCCESS)
1299 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1300 "%3d:%02d", mnsc(repdata->taper.sec));
1302 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1304 CheckStringMax(&ColumnData[TapeTime], TimeRateBuffer);
1306 if(repdata->taper.result == L_SUCCESS ||
1307 repdata->taper.result == L_CHUNKSUCCESS)
1308 CheckFloatMax(&ColumnData[TapeRate], repdata->taper.kps);
1310 CheckStringMax(&ColumnData[TapeRate], "N/A ");
1317 output_summary(void)
1321 char *ds="DUMPER STATS";
1322 char *ts=" TAPER STATS";
1325 int i, h, w1, wDump, wTape;
1326 double outsize, origsize;
1329 HostName = StringToColumn("HostName");
1330 Disk = StringToColumn("Disk");
1331 Level = StringToColumn("Level");
1332 OrigKB = StringToColumn("OrigKB");
1333 OutKB = StringToColumn("OutKB");
1334 Compress = StringToColumn("Compress");
1335 DumpTime = StringToColumn("DumpTime");
1336 DumpRate = StringToColumn("DumpRate");
1337 TapeTime = StringToColumn("TapeTime");
1338 TapeRate = StringToColumn("TapeRate");
1340 /* at first determine if we have to recalculate our widths */
1341 if (MaxWidthsRequested)
1344 /* title for Dumper-Stats */
1345 w1= ColWidth(HostName, Level);
1346 wDump= ColWidth(OrigKB, DumpRate);
1347 wTape= ColWidth(TapeTime, TapeRate);
1349 /* print centered top titles */
1350 h = (int)strlen(ds);
1356 g_fprintf(mailf, "%*s", w1+h, "");
1357 g_fprintf(mailf, "%-*s", wDump-h, ds);
1358 h = (int)strlen(ts);
1364 g_fprintf(mailf, "%*s", h, "");
1365 g_fprintf(mailf, "%-*s", wTape-h, ts);
1368 /* print the titles */
1369 for (i=0; ColumnData[i].Name != NULL; i++) {
1371 ColumnInfo *cd= &ColumnData[i];
1372 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1373 if (cd->Format[1] == '-')
1377 if(strcmp(cd->Title,"ORIG-KB") == 0) {
1378 /* cd->Title must be re-allocated in write-memory */
1379 cd->Title = stralloc("ORIG-KB");
1380 cd->Title[5] = displayunit[0];
1382 if(strcmp(cd->Title,"OUT-KB") == 0) {
1383 /* cd->Title must be re-allocated in write-memory */
1384 cd->Title = stralloc("OUT-KB");
1385 cd->Title[4] = displayunit[0];
1387 g_fprintf(mailf, fmt, cd->Width, cd->Title);
1391 /* print the rules */
1392 fputs(tmp=Rule(HostName, Level), mailf); amfree(tmp);
1393 fputs(tmp=Rule(OrigKB, DumpRate), mailf); amfree(tmp);
1394 fputs(tmp=Rule(TapeTime, TapeRate), mailf); amfree(tmp);
1397 for(dp = sortq.head; dp != NULL; dp = dp->next) {
1400 char TimeRateBuffer[40];
1401 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1406 cd= &ColumnData[HostName];
1407 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1408 g_fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->host->hostname);
1410 cd= &ColumnData[Disk];
1411 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1412 devname = sanitize_string(dp->name);
1413 qdevname = quote_string(devname);
1414 devlen = strlen(qdevname);
1415 if (devlen > (size_t)cd->Width) {
1417 g_fprintf(mailf, cd->Format, cd->Width-1, cd->Precision-1,
1418 qdevname+devlen - (cd->Width-1) );
1421 g_fprintf(mailf, cd->Format, cd->Width, cd->Width, qdevname);
1424 cd= &ColumnData[Level];
1425 if (repdata->dumper.result == L_BOGUS &&
1426 repdata->taper.result == L_BOGUS) {
1428 g_fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1429 tmp=TextRule(OrigKB, TapeRate, "NO FILE TO FLUSH"));
1431 g_fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1432 tmp=TextRule(OrigKB, TapeRate, "MISSING"));
1438 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1439 g_fprintf(mailf, cd->Format, cd->Width, cd->Precision,repdata->level);
1441 if (repdata->dumper.result == L_SKIPPED) {
1442 g_fprintf(mailf, "%s\n",
1443 tmp=TextRule(OrigKB, TapeRate, "SKIPPED"));
1447 if (repdata->dumper.result == L_FAIL && (repdata->chunker.result != L_PARTIAL && repdata->taper.result != L_PARTIAL)) {
1448 g_fprintf(mailf, "%s\n",
1449 tmp=TextRule(OrigKB, TapeRate, "FAILED"));
1451 exit_status |= STATUS_FAILED;
1455 if(repdata->dumper.result == L_SUCCESS ||
1456 repdata->dumper.result == L_CHUNKSUCCESS)
1457 origsize = repdata->dumper.origsize;
1458 else if(repdata->taper.result == L_SUCCESS ||
1459 repdata->taper.result == L_PARTIAL)
1460 origsize = repdata->taper.origsize;
1462 origsize = repdata->chunker.origsize;
1464 if(repdata->taper.result == L_SUCCESS ||
1465 repdata->taper.result == L_CHUNKSUCCESS)
1466 outsize = repdata->taper.outsize;
1467 else if(repdata->chunker.result == L_SUCCESS ||
1468 repdata->chunker.result == L_PARTIAL ||
1469 repdata->chunker.result == L_CHUNKSUCCESS)
1470 outsize = repdata->chunker.outsize;
1471 else if (repdata->taper.result == L_PARTIAL)
1472 outsize = repdata->taper.outsize;
1474 outsize = repdata->dumper.outsize;
1476 cd= &ColumnData[OrigKB];
1477 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1478 if(isnormal(origsize))
1479 g_fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(origsize));
1481 g_fprintf(mailf, "%*.*s", cd->Width, cd->Width, "N/A");
1483 cd= &ColumnData[OutKB];
1484 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1486 g_fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(outsize));
1488 cd= &ColumnData[Compress];
1489 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1491 if(abs(outsize - origsize) < 32)
1493 else if(origsize < 1.0)
1498 fputs(sDivZero(pct(outsize), f, Compress), mailf);
1500 cd= &ColumnData[DumpTime];
1501 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1502 if(repdata->dumper.result == L_SUCCESS ||
1503 repdata->dumper.result == L_CHUNKSUCCESS)
1504 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1505 "%3d:%02d", mnsc(repdata->dumper.sec));
1507 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1509 g_fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
1511 cd= &ColumnData[DumpRate];
1512 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1513 if(repdata->dumper.result == L_SUCCESS ||
1514 repdata->dumper.result == L_CHUNKSUCCESS)
1515 g_fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->dumper.kps);
1517 g_fprintf(mailf, "%*s", cd->Width, "N/A ");
1519 cd= &ColumnData[TapeTime];
1520 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1521 if(repdata->taper.result == L_FAIL) {
1522 g_fprintf(mailf, "%s\n",
1523 tmp=TextRule(TapeTime, TapeRate, "FAILED "));
1528 if(repdata->taper.result == L_SUCCESS ||
1529 repdata->taper.result == L_PARTIAL ||
1530 repdata->taper.result == L_CHUNKSUCCESS)
1531 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1532 "%3d:%02d", mnsc(repdata->taper.sec));
1534 g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1536 g_fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
1538 cd= &ColumnData[TapeRate];
1539 g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
1540 if(repdata->taper.result == L_SUCCESS ||
1541 repdata->taper.result == L_PARTIAL ||
1542 repdata->taper.result == L_CHUNKSUCCESS)
1543 g_fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->taper.kps);
1545 g_fprintf(mailf, "%*s", cd->Width, "N/A ");
1547 if (repdata->chunker.result == L_PARTIAL)
1548 g_fprintf(mailf, " PARTIAL");
1549 else if(repdata->taper.result == L_PARTIAL)
1550 g_fprintf(mailf, " TAPE-PARTIAL");
1560 const char *err_text)
1563 s = g_strdup_printf(_("line %d of log is bogus: <%s %s %s>\n"),
1565 logtype_str[curlog], program_str[curprog], curstr);
1566 g_printf("%s\n", s);
1567 g_printf(_(" Scan failed at: <%s>\n"), err_text);
1568 addline(&errsum, s);
1574 * Formats an integer of the form YYYYMMDDHHMMSS into the string
1575 * "Monthname DD, YYYY". A pointer to the statically allocated string
1576 * is returned, so it must be copied to other storage (or just printed)
1577 * before calling nicedate() again.
1581 const char *datestamp)
1583 static char nice[64];
1586 static char *months[13] = {
1601 int year, month, day;
1603 strncpy(date, datestamp, 8);
1605 numdate = atoi(date);
1606 year = numdate / 10000;
1607 day = numdate % 100;
1608 month = (numdate / 100) % 100;
1612 g_snprintf(nice, SIZEOF(nice), "%s %d, %d", _(months[month]), day, year);
1620 static int started = 0;
1630 skip_whitespace(s, ch);
1631 if(ch == '\0' || strncmp_const_skip(s - 1, "datestamp", s, ch) != 0) {
1636 skip_whitespace(s, ch);
1642 skip_non_whitespace(s, ch);
1644 run_datestamp = newstralloc(run_datestamp, fp);
1647 skip_whitespace(s, ch);
1648 if(ch == '\0' || strncmp_const_skip(s - 1, "label", s, ch) != 0) {
1653 skip_whitespace(s, ch);
1659 skip_non_whitespace(s, ch);
1662 label = stralloc(fp);
1666 fp = vstralloc(tape_labels, ", ", label, NULL);
1667 amfree(tape_labels);
1670 tape_labels = stralloc(label);
1675 if(stats_by_tape == NULL) {
1676 stats_by_tape = current_tape = (taper_t *)alloc(SIZEOF(taper_t));
1679 current_tape->next = (taper_t *)alloc(SIZEOF(taper_t));
1680 current_tape = current_tape->next;
1682 current_tape->label = label;
1683 current_tape->taper_time = 0.0;
1684 current_tape->coutsize = 0.0;
1685 current_tape->corigsize = 0.0;
1686 current_tape->tapedisks = 0;
1687 current_tape->tapechunks = 0;
1688 current_tape->next = NULL;
1708 skip_whitespace(s, ch);
1709 if(ch == '\0' || strncmp_const_skip(s - 1, "date", s, ch) != 0) {
1710 return; /* ignore bogus line */
1713 skip_whitespace(s, ch);
1719 skip_non_whitespace(s, ch);
1721 run_datestamp = newstralloc(run_datestamp, fp);
1726 if(amflush_run && normal_run) {
1729 _(" reporter: both amflush and planner output in log, ignoring amflush."));
1741 if(curprog == P_DRIVER || curprog == P_AMFLUSH || curprog == P_PLANNER) {
1745 skip_whitespace(s, ch);
1746 if(ch == '\0' || strncmp_const_skip(s - 1, "date", s, ch) != 0) {
1751 skip_whitespace(s, ch);
1756 skip_non_whitespace(s, ch); /* ignore the date string */
1758 skip_whitespace(s, ch);
1759 if(ch == '\0' || strncmp_const_skip(s - 1, "time", s, ch) != 0) {
1760 /* older planner doesn't write time */
1761 if(curprog == P_PLANNER) return;
1766 skip_whitespace(s, ch);
1771 if(sscanf(s - 1, "%lf", &a_time) != 1) {
1775 if(curprog == P_PLANNER) {
1776 planner_time = a_time;
1779 total_time = a_time;
1790 char *hostname, *diskname, *datestamp, *qdiskname;
1792 double sec, kps, nbytes, cbytes;
1796 if(curprog == P_DRIVER) {
1800 skip_whitespace(s, ch);
1801 if(ch != '\0' && strncmp_const_skip(s - 1, "startup time", s, ch) == 0) {
1802 skip_whitespace(s, ch);
1807 if(sscanf(s - 1, "%lf", &startup_time) != 1) {
1811 planner_time = startup_time;
1813 else if(ch != '\0' && strncmp_const_skip(s - 1, "hostname", s, ch) == 0) {
1814 skip_whitespace(s, ch);
1819 ghostname = stralloc(s-1);
1821 else if(ch != '\0' && strncmp_const_skip(s - 1, "estimate", s, ch) == 0) {
1822 skip_whitespace(s, ch);
1828 skip_non_whitespace(s, ch);
1830 hostname = stralloc(fp);
1833 skip_whitespace(s, ch);
1841 skip_quoted_string(s, ch);
1843 diskname = unquote_string(qdiskname);
1846 skip_whitespace(s, ch);
1854 skip_non_whitespace(s, ch);
1856 datestamp = stralloc(fp);
1858 skip_whitespace(s, ch);
1860 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1867 skip_integer(s, ch);
1868 if(level < 0 || level > 9) {
1875 skip_whitespace(s, ch);
1877 if(sscanf(s - 1,"[sec %lf nkb %lf ckb %lf kps %lf",
1878 &sec, &nbytes, &cbytes, &kps) != 4) {
1886 dp = lookup_disk(hostname, diskname);
1888 addtoX_summary(&first_failed, &last_failed,
1889 hostname, diskname, level,
1890 _("ERROR [not in disklist]"));
1891 exit_status |= STATUS_FAILED;
1898 repdata = find_repdata(dp, datestamp, level);
1900 repdata->est_nsize = nbytes;
1901 repdata->est_csize = cbytes;
1922 str = vstrallocf(" %s: %s", program_str[curprog], curstr);
1923 addline(¬es, str);
1933 char *s = NULL, *nl;
1936 if(curlog == L_ERROR && curprog == P_TAPER) {
1940 skip_whitespace(s, ch);
1941 if(ch != '\0' && strncmp_const_skip(s - 1, "no-tape", s, ch) == 0) {
1942 skip_whitespace(s, ch);
1944 if((nl = strchr(s - 1, '\n')) != NULL) {
1947 tapestart_error = newstralloc(tapestart_error, s - 1);
1950 exit_status |= STATUS_TAPE;;
1953 /* else some other tape error, handle like other errors */
1955 /* else some other tape error, handle like other errors */
1957 s = vstrallocf(" %s: %s %s", program_str[curprog],
1958 logtype_str[curlog], curstr);
1959 addline(&errsum, s);
1966 handle_summary(void)
1973 static int nb_disk=0;
1978 char *s, *fp, *qdiskname;
1980 char *hostname = NULL, *diskname = NULL;
1982 if(curprog != P_PLANNER && curprog != P_AMFLUSH) {
1988 for(dp = diskq.head; dp != NULL; dp = dp->next)
1996 skip_whitespace(s, ch);
2002 skip_non_whitespace(s, ch);
2004 hostname = newstralloc(hostname, fp);
2007 skip_whitespace(s, ch);
2014 skip_quoted_string(s, ch);
2016 diskname = unquote_string(qdiskname);
2019 dp = lookup_disk(hostname, diskname);
2021 dp = add_disk(&diskq, hostname, diskname);
2029 /* XXX Just a placeholder, in case we decide to do something with L_CHUNK
2030 * log entries. Right now they're just the equivalent of L_SUCCESS, but only
2031 * for a split chunk of the overall dumpfile.
2038 double sec, kps, kbytes;
2043 char *hostname = NULL;
2044 char *diskname = NULL;
2052 if(curprog != P_TAPER) {
2060 skip_whitespace(s, ch);
2066 if (logtype == L_PART || logtype == L_PARTPARTIAL) {
2068 skip_non_whitespace(s, ch);
2070 label = stralloc(fp);
2073 skip_whitespace(s, ch);
2074 if(ch == '\0' || sscanf(s - 1, "%d", &fileno) != 1) {
2079 skip_integer(s, ch);
2080 skip_whitespace(s, ch);
2090 skip_non_whitespace(s, ch);
2092 hostname = stralloc(fp);
2095 skip_whitespace(s, ch);
2102 skip_quoted_string(s, ch);
2104 diskname = unquote_string(fp);
2107 skip_whitespace(s, ch);
2115 skip_non_whitespace(s, ch);
2117 datestamp = stralloc(fp);
2120 skip_whitespace(s, ch);
2121 if(ch == '\0' || sscanf(s - 1, "%d", &chunk) != 1) {
2128 skip_integer(s, ch);
2130 if (ch != '\0' && s[-1] == '/') {
2132 if (sscanf(s - 1, "%d", &totpart) != 1) {
2139 skip_integer(s, ch);
2142 skip_whitespace(s, ch);
2143 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2150 skip_integer(s, ch);
2153 if(level < 0 || level > 9) {
2161 skip_whitespace(s, ch);
2162 if(sscanf(s - 1,"[sec %lf kb %lf kps %lf", &sec, &kbytes, &kps) != 3) {
2171 dp = lookup_disk(hostname, diskname);
2175 str = vstrallocf(_(" %s ERROR [not in disklist]"),
2176 prefix(hostname, diskname, level));
2177 addline(&errsum, str);
2185 repdata = find_repdata(dp, datestamp, level);
2187 sp = &(repdata->taper);
2195 if(current_tape == NULL) {
2196 error("current_tape == NULL");
2198 if (sp->filenum == 0) {
2199 sp->filenum = ++tapefcount;
2200 sp->tapelabel = current_tape->label;
2202 tapechunks[level] +=1;
2203 stats[i].tapechunks +=1;
2204 current_tape->taper_time += sec;
2205 current_tape->coutsize += kbytes;
2206 current_tape->tapechunks += 1;
2217 double kbytes = 0.0;
2218 double origkb = 0.0;
2221 char *s, *fp, *qdiskname;
2223 char *hostname = NULL;
2224 char *diskname = NULL;
2232 if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER &&
2233 curprog != P_CHUNKER) {
2241 skip_whitespace(s, ch);
2247 skip_non_whitespace(s, ch);
2249 hostname = stralloc(fp);
2252 skip_whitespace(s, ch);
2259 skip_quoted_string(s, ch);
2261 diskname = unquote_string(qdiskname);
2263 skip_whitespace(s, ch);
2271 skip_non_whitespace(s, ch);
2273 datestamp = stralloc(fp);
2276 //datestamp is optional
2277 if(strlen(datestamp) < 6) {
2278 totpart = atoi(datestamp);
2279 datestamp = newstralloc(datestamp, run_datestamp);
2282 skip_whitespace(s, ch);
2283 if(ch == '\0' || sscanf(s - 1, "%d", &totpart) != 1) {
2290 skip_integer(s, ch);
2293 skip_whitespace(s, ch);
2295 //totpart is optional
2298 if (*(s-1) == '[') {
2302 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2309 skip_integer(s, ch);
2310 skip_whitespace(s, ch);
2314 if(level < 0 || level > 9) {
2321 /* Planner success messages (for skipped
2322 dumps) do not contain statistics */
2323 if(curprog != P_PLANNER) {
2326 if((curprog != P_DUMPER)
2327 || (sscanf(s - 1,"[sec %lf kb %lf kps %lf orig-kb %lf",
2328 &sec, &kbytes, &kps, &origkb) != 4)) {
2330 if(sscanf(s - 1,"[sec %lf kb %lf kps %lf",
2331 &sec, &kbytes, &kps) != 3) {
2340 if(!isnormal(origkb))
2346 dp = lookup_disk(hostname, diskname);
2348 addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
2349 _("ERROR [not in disklist]"));
2350 exit_status |= STATUS_FAILED;
2357 repdata = find_repdata(dp, datestamp, level);
2359 if(curprog == P_PLANNER) {
2360 repdata->dumper.result = L_SKIPPED;
2367 if(curprog == P_TAPER)
2368 sp = &(repdata->taper);
2369 else if(curprog == P_DUMPER)
2370 sp = &(repdata->dumper);
2371 else sp = &(repdata->chunker);
2375 if (origkb < 0.0 && (curprog == P_CHUNKER || curprog == P_TAPER) &&
2376 isnormal(repdata->dumper.outsize)) {
2377 /* take origkb from DUMPER line */
2378 origkb = repdata->dumper.outsize;
2379 } else if (origkb < 0.0) {
2380 /* take origkb from infofile, needed for amflush */
2385 get_info(hostname, diskname, &inf);
2386 tm = localtime(&inf.inf[level].date);
2388 Idatestamp = 10000*(tm->tm_year+1900) +
2389 100*(tm->tm_mon+1) + tm->tm_mday;
2391 Idatestamp = 19000101;
2394 if(atoi(datestamp) == Idatestamp) {
2395 /* grab original size from record */
2396 origkb = (double)inf.inf[level].size;
2402 if (curprog == P_DUMPER &&
2403 (sp->result == L_FAIL || sp->result == L_PARTIAL)) {
2404 addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
2405 _("was successfully retried"));
2412 sp->result = L_SUCCESS;
2413 sp->datestamp = repdata->datestamp;
2416 sp->origsize = origkb;
2417 sp->outsize = kbytes;
2419 if(curprog == P_TAPER) {
2420 if(current_tape == NULL) {
2421 error(_("current_tape == NULL"));
2424 stats[i].taper_time += sec;
2425 sp->filenum = ++tapefcount;
2426 sp->tapelabel = current_tape->label;
2427 sp->totpart = totpart;
2428 tapedisks[level] +=1;
2429 stats[i].tapedisks +=1;
2430 stats[i].tapesize += kbytes;
2431 sp->outsize = kbytes;
2432 if(!isnormal(repdata->chunker.outsize) && isnormal(repdata->dumper.outsize)) { /* dump to tape */
2433 stats[i].outsize += kbytes;
2434 if (abs(kbytes - origkb) >= 32) {
2435 /* server compressed */
2436 stats[i].corigsize += origkb;
2437 stats[i].coutsize += kbytes;
2440 current_tape->tapedisks += 1;
2443 if(curprog == P_DUMPER) {
2444 stats[i].dumper_time += sec;
2445 if (abs(kbytes - origkb) < 32) {
2446 /* not client compressed */
2447 sp->origsize = kbytes;
2450 /* client compressed */
2451 stats[i].corigsize += sp->origsize;
2452 stats[i].coutsize += kbytes;
2454 dumpdisks[level] +=1;
2455 stats[i].dumpdisks +=1;
2456 stats[i].origsize += sp->origsize;
2459 if(curprog == P_CHUNKER) {
2460 sp->outsize = kbytes;
2461 stats[i].outsize += kbytes;
2462 if (abs(kbytes - origkb) >= 32) {
2463 /* server compressed */
2464 stats[i].corigsize += origkb;
2465 stats[i].coutsize += kbytes;
2472 handle_partial(void)
2477 repdata = handle_success(L_PARTIAL);
2481 if(curprog == P_TAPER)
2482 sp = &(repdata->taper);
2483 else if(curprog == P_DUMPER)
2484 sp = &(repdata->dumper);
2485 else sp = &(repdata->chunker);
2487 sp->result = L_PARTIAL;
2491 handle_strange(void)
2494 char *strangestr = NULL;
2498 repdata = handle_success(L_SUCCESS);
2502 qdisk = quote_string(repdata->disk->name);
2504 addline(&strangedet,"");
2505 str = vstrallocf("/-- %s STRANGE",
2506 prefix(repdata->disk->host->hostname, qdisk, repdata->level));
2507 addline(&strangedet, str);
2510 while(contline_next()) {
2512 get_logline(logfile);
2514 if(strncmp_const_skip(curstr, "sendbackup: warning ", s, ch) == 0) {
2515 strangestr = newstralloc(strangestr, s);
2517 addline(&strangedet, curstr);
2519 addline(&strangedet,"\\--------");
2521 str = vstrallocf("STRANGE %s", strangestr? strangestr : _("(see below)"));
2522 addtoX_summary(&first_strange, &last_strange,
2523 repdata->disk->host->hostname, qdisk, repdata->level, str);
2524 exit_status |= STATUS_STRANGE;
2539 char *s, *fp, *qdiskname;
2551 skip_whitespace(s, ch);
2557 skip_non_whitespace(s, ch);
2560 skip_whitespace(s, ch);
2566 skip_quoted_string(s, ch);
2568 diskname = unquote_string(qdiskname);
2570 skip_whitespace(s, ch);
2577 skip_non_whitespace(s, ch);
2579 datestamp = stralloc(fp);
2581 if(strlen(datestamp) < 3) { /* there is no datestamp, it's the level */
2582 level = atoi(datestamp);
2583 datestamp = newstralloc(datestamp, run_datestamp);
2585 else { /* read the level */
2586 skip_whitespace(s, ch);
2587 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2593 skip_integer(s, ch);
2596 skip_whitespace(s, ch);
2604 if((s = strchr(errstr, '\n')) != NULL) {
2608 dp = lookup_disk(hostname, diskname);
2611 addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
2612 _("ERROR [not in disklist]"));
2614 repdata = find_repdata(dp, datestamp, level);
2616 if(curprog == P_TAPER)
2617 sp = &(repdata->taper);
2618 else sp = &(repdata->dumper);
2620 if(sp->result != L_SUCCESS)
2621 sp->result = L_FAIL;
2625 str = vstrallocf(_("FAILED %s"), errstr);
2626 addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
2630 if(curprog == P_DUMPER) {
2631 addline(&errdet,"");
2632 str = vstrallocf("/-- %s FAILED %s",
2633 prefix(hostname, qdiskname, level),
2635 addline(&errdet, str);
2637 while(contline_next()) {
2638 get_logline(logfile);
2639 addline(&errdet, curstr);
2641 addline(&errdet,"\\--------");
2642 exit_status |= STATUS_FAILED;
2649 generate_missing(void)
2654 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2655 if(dp->todo && data(dp) == NULL) {
2656 qdisk = quote_string(dp->name);
2657 addtoX_summary(&first_failed, &last_failed, dp->host->hostname,
2658 qdisk, -987, _("RESULTS MISSING"));
2659 exit_status |= STATUS_MISSING;
2666 generate_bad_estimate(void)
2673 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2675 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
2676 if(repdata->est_csize >= 0.1) {
2677 if(repdata->taper.result == L_SUCCESS ||
2678 repdata->taper.result == L_CHUNKSUCCESS)
2679 outsize = repdata->taper.outsize;
2680 else if(repdata->chunker.result == L_SUCCESS ||
2681 repdata->chunker.result == L_PARTIAL ||
2682 repdata->chunker.result == L_CHUNKSUCCESS)
2683 outsize = repdata->chunker.outsize;
2684 else if(repdata->taper.result == L_PARTIAL)
2685 outsize = repdata->taper.outsize;
2687 outsize = repdata->dumper.outsize;
2689 if(repdata->est_csize * 0.9 > outsize) {
2691 _(" big estimate: %s %s %d"),
2692 repdata->disk->host->hostname,
2693 repdata->disk->name,
2698 _(" est: %.0lf%s out %.0lf%s"),
2699 du(repdata->est_csize), displayunit,
2700 du(outsize), displayunit);
2704 else if(repdata->est_csize * 1.1 < outsize) {
2706 _(" small estimate: %s %s %d"),
2707 repdata->disk->host->hostname,
2708 repdata->disk->name,
2713 _(" est: %.0lf%s out %.0lf%s"),
2714 du(repdata->est_csize), displayunit,
2715 du(outsize), displayunit);
2731 static char *str = NULL;
2733 if (level == -987) {
2734 str = newvstrallocf(str, " %s %s",
2735 host ? host : _("(host?)"),
2736 disk ? disk : _("(disk?)"));
2738 str = newvstrallocf(str, " %s %s lev %d",
2739 host ? host : _("(host?)"),
2740 disk ? disk : _("(disk?)"),
2757 static char *str = NULL;
2759 h=alloc(len_host+1);
2761 strncpy(h, host, len_host);
2763 strncpy(h, _("(host?)"), len_host);
2766 for(l = strlen(h); l < len_host; l++) {
2769 d=alloc(len_disk+1);
2771 strncpy(d, disk, len_disk);
2773 strncpy(d, _("(disk?)"), len_disk);
2776 for(l = strlen(d); l < len_disk; l++) {
2779 if (level == -987) {
2780 str = newvstrallocf(str, " %s %s", h, d);
2782 str = newvstrallocf(str, " %s %s lev %d", h, d, level);
2792 X_summary_t **first,
2799 X_summary_t *X_summary;
2801 X_summary = alloc(SIZEOF(X_summary_t));
2802 X_summary->hostname = stralloc(host);
2803 X_summary->diskname = stralloc(disk);
2804 X_summary->level = level;
2805 X_summary->str = stralloc(str);
2806 X_summary->next = NULL;
2807 if (*first == NULL) {
2811 (*last)->next = X_summary;
2824 lbl_templ = config_dir_relative(lbl_templ);
2825 if ((fd = open(lbl_templ, 0)) < 0) {
2827 curprog = P_REPORTER;
2828 curstr = vstrallocf(_("could not open PostScript template file %s: %s"),
2829 lbl_templ, strerror(errno));
2833 afclose(postscript);
2836 while ((numread = read(fd, buf, SIZEOF(buf))) > 0) {
2837 if (fwrite(buf, (size_t)numread, 1, postscript) != 1) {
2839 curprog = P_REPORTER;
2840 curstr = vstrallocf(_("error copying PostScript template file %s: %s"),
2841 lbl_templ, strerror(errno));
2845 afclose(postscript);
2851 curprog = P_REPORTER;
2852 curstr = vstrallocf(_("error reading PostScript template file %s: %s"),
2853 lbl_templ, strerror(errno));
2857 afclose(postscript);
2866 /*@keep@*/ disk_t *dp,
2870 repdata_t *repdata, *prev;
2873 datestamp = run_datestamp;
2875 for(repdata = data(dp); repdata != NULL && (repdata->level != level || strcmp(repdata->datestamp,datestamp)!=0); repdata = repdata->next) {
2879 repdata = (repdata_t *)alloc(SIZEOF(repdata_t));
2880 memset(repdata, '\0', SIZEOF(repdata_t));
2882 repdata->datestamp = stralloc(datestamp ? datestamp : "");
2883 repdata->level = level;
2884 repdata->dumper.result = L_BOGUS;
2885 repdata->taper.result = L_BOGUS;
2886 repdata->next = NULL;
2888 prev->next = repdata;
2890 dp->up = (void *)repdata;
2897 do_postscript_output(void)
2899 tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
2902 double outsize, origsize;
2909 tapesize = tapetype_get_length(tp);
2910 marksize = tapetype_get_filemark(tp);
2912 for(current_tape = stats_by_tape; current_tape != NULL;
2913 current_tape = current_tape->next) {
2915 if (current_tape->label == NULL) {
2919 copy_template_file(tapetype_get_lbl_templ(tp));
2921 if (postscript == NULL)
2924 /* generate a few elements */
2925 g_fprintf(postscript,"(%s) DrawDate\n\n",
2926 nicedate(run_datestamp ? run_datestamp : "0"));
2927 g_fprintf(postscript,_("(Amanda Version %s) DrawVers\n"),version());
2928 g_fprintf(postscript,"(%s) DrawTitle\n", current_tape->label);
2931 g_fprintf(postscript, "(Total Size: %6.1lf MB) DrawStat\n",
2932 mb(current_tape->coutsize));
2933 g_fprintf(postscript, _("(Tape Used (%%) "));
2934 divzero(postscript, pct(current_tape->coutsize +
2935 marksize * (current_tape->tapedisks + current_tape->tapechunks)),
2937 g_fprintf(postscript," %%) DrawStat\n");
2938 g_fprintf(postscript, _("(Compression Ratio: "));
2939 divzero(postscript, pct(current_tape->coutsize),current_tape->corigsize);
2940 g_fprintf(postscript," %%) DrawStat\n");
2941 g_fprintf(postscript,_("(Filesystems Taped: %4d) DrawStat\n"),
2942 current_tape->tapedisks);
2946 g_fprintf(postscript,
2947 "(-) (%s) (-) ( 0) ( 32) ( 32) DrawHost\n",
2948 current_tape->label);
2950 for(dp = sortq.head; dp != NULL; dp = dp->next) {
2951 if (dp->todo == 0) {
2954 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
2956 if(repdata->taper.tapelabel != current_tape->label) {
2960 if(repdata->dumper.result == L_SUCCESS ||
2961 repdata->dumper.result == L_PARTIAL)
2962 origsize = repdata->dumper.origsize;
2964 origsize = repdata->taper.origsize;
2966 if(repdata->taper.result == L_SUCCESS ||
2967 repdata->taper.result == L_PARTIAL)
2968 outsize = repdata->taper.outsize;
2970 outsize = repdata->dumper.outsize;
2972 if (repdata->taper.result == L_SUCCESS ||
2973 repdata->taper.result == L_PARTIAL) {
2974 if(isnormal(origsize)) {
2975 g_fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8.0lf) (%8.0lf) DrawHost\n",
2976 dp->host->hostname, dp->name, repdata->level,
2977 repdata->taper.filenum, origsize,
2981 g_fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8s) (%8.0lf) DrawHost\n",
2982 dp->host->hostname, dp->name, repdata->level,
2983 repdata->taper.filenum, "N/A",
2990 g_fprintf(postscript,"\nshowpage\n");